7

Supposing that I have bidimensional matrix n x n, and we already know n=4 and values 1 - 16 like this

I must multiply the parts like:

Array1*Array2 = result

Array3*result = result1

Array4*result1 = result2

ShowMatrix()
{
echo "MyMatrix is:"
for((i=0;i<$n;i++))do
   for((j=0;j<$n;j++))do
printf '  '${myArray[i*n+j]}
done
printf '\n';
done
}

I tried to divide it into 4 parts like in the schema, and assign each part to Array1, Array2, Array3 and Array4

cut1()
{
for((i=0;i<$n/2;i++))do
   for((j=0;j<$n/2;j++))do
Array1[i*n+j]=${myArray[i*n+j]}
done
done
}

cut2()
{
for((i=0;i<$n/2;i++))do
   for((j=$n/2;j<$n;j++))do
Array2[i*n+j]=${myArray[i*n+j]}
done
done
}

cut3()
{
for((i=$n/2;i<$n;i++))do
   for((j=0;j<$n/2;j++))do
Array3[i*n+j]=${myArray[i*n+j]}
done
done
}

cut4()
{
for((i=$n/2;i<$n;i++))do
   for((j=$n/2;j<$n;j++))do
Array4[i*n+j]=${myArray[i*n+j]}
done
done
}

After that, I tried to multiply them like in the schema:

Array1*Array2 = result

Array3*result = result1

Array4*result1 = result2

multiply()
{
 for((i=0;i<$n;i++))do
   for((j=0;j<$n;j++))do
     result[i*n+j]=0
     for((k=0;k<$n;k++))do
let "result[i*n+j]=${result[i*n+j]}+${Array1[i*n+k]}*${Array2[k*n+j]}"
done
done
done
}

multiply1()
{
 for((i=0;i<$n;i++))do
   for((j=0;j<$n;j++))do
     result1[i*n+j]=0
     for((k=0;k<$n;k++))do
let "result1[i*n+j]=${result1[i*n+j]}+${result[i*n+k]}*${Array3[k*n+j]}"
done
done
done
}

multiply2()
{
 for((i=0;i<$n;i++))do
   for((j=0;j<$n;j++))do
     result2[i*n+j]=0
     for((k=0;k<$n;k++))do
let "result2[i*n+j]=${result2[i*n+j]}+${result1[i*n+k]}*${Array4[k*n+j]}"
done
done
done
}

And the results, after I called functions:

cut1
cut2
cut3
cut4
multiply
multiply1
multiply2


echo "result is:"
for((i=0;i<$n;i++))do
   for((j=0;j<$n;j++))do
printf '  '${result[i*n+j]}
done
printf '\n';
done

echo "result1 is:"
for((i=0;i<$n;i++))do
   for((j=0;j<$n;j++))do
printf '  '${result1[i*n+j]}
done
printf '\n';
done

echo "result2 is:"
for((i=0;i<$n;i++))do
   for((j=0;j<$n;j++))do
printf '  '${result2[i*n+j]}
done
printf '\n';
done
Community
  • 1
  • 1
Choletski
  • 7,074
  • 6
  • 43
  • 64
  • 3
    IMNSHO, Bash is not an appropriate language for matrix multiplication. You can probably do it, but it ain't easy. You can probably do it in `sed` too, since `sed` is computationally complete; however, that would be even more ludicrous than using Bash. – Jonathan Leffler Jan 09 '15 at 22:04
  • The main condition is to use bash! But the mistake is in logical part not in coding here, that why I am asking. – Choletski Jan 09 '15 at 22:09
  • 2
    If you want to do multiplication in the subscripts, `${variable[$((3*2+2))]}` works (accesses element 8), and so does `${variable[$(($i*$n+$j))]}` and even `${variable[$((i*n+j))]}` for suitable values of `i`, `n`, and `j`. I still don't think it is a good idea to use Bash for this task, though -- but sadistic teachers evidently think otherwise. – Jonathan Leffler Jan 09 '15 at 22:15
  • @JonathanLeffler: Actually, for array variables, the computation of the subscript is *arithmetic*, so you don't need the `$((...))` nor the dollar signs. See [bash manual section 6.7](http://www.gnu.org/software/bash/manual/bashref.html#Arrays-1): "The subscript is treated as an arithmetic expression that must evaluate to a number." – rici Jan 09 '15 at 23:04
  • @rici: gotta love the lack of orthogonality. Sometimes it does happen; sometimes it doesn't; and there's no way to know which applies without reading every damn word in the manual. There are too many exceptions to the rules. OK; I don't know what the trouble is with the code, but I'm not going to experiment with it. I'm in two minds about whether to remove my other comment (this one will probably shuffle off this mortal coil shortly). – Jonathan Leffler Jan 09 '15 at 23:08
  • @Choletski: "It isn't working" is about as non-specific an error report as you can get; it provides no information. Do you get an error message? Is the result unexpected? (And if so, what did you expect and what did you get?) Or does it just lie on the couch and watch TV? – rici Jan 09 '15 at 23:09
  • @JonathanLeffler: I understand your frustration. Generally speaking, bash uses arithmetic evaluation in numeric contexts: subscripts (of ordinary arrays, not those declared associative); `for ((...))` loops; and assignment to variables declared numeric (with `declare -i`). If you declare your variables (thus making them local to your functions) then `bash` arithmetic becomes fairly readable. – rici Jan 09 '15 at 23:14
  • @rich Actually the problem is logical. Look, I have 4x4 matrix => 16 values, when I am trying to get only 4 of them (in methods cut1-4) I get also 4x4 matrix with 4 real values and 12 null, that's why I can't multiply them. So I need to obtain only a matrix 2x2 => 4 valid values in each method cut1-4. When I execute my code, result, result1 and result2 are 0 – Choletski Jan 09 '15 at 23:23
  • @Choletski: Your analysis is completely correct. I don't see why you find it difficult to fix, assuming you actually wrote the code you pasted into your question, since it is just a question of computing subscripts correctly for an n/2 x n/2 matrix. – rici Jan 09 '15 at 23:54
  • From the bash manual: Bash provides one-dimensional indexed and associative array variables. What kind of bi-dimensional matrix do you have? In your examples, there is only a single subscript for the array (e.g. var[] vs. var[][]) In your diagram, n=2, not n=4 ? If you can provide code that initializes myArray with actual values, we might be able to help more. – Jesse Parker Jan 19 '15 at 18:33
  • can you get away with using any perl in there, that would help quite a bit, in my years using bash for high level stuff, it is a TERRIBLE idea to do this.... however, I hope you are at least using bashdb, and if actually get it to work, ddd, it will help visualize your problem instead of having to use a bunch of echo or printf statements. Arrays are tricky in bash and behaviors of can be modified by your shopts, etc. Like i said, naughty stuff... I would cheat and wrap a perl program in a here document inside a bash script and hope nobody cares :P – osirisgothra Feb 06 '15 at 15:36

1 Answers1

1

The main conceptual problem with the code as written is that you divide a large array into smaller pieces but then don't adjust for the reduced size of those pieces.

Specifically, one definite bug is in the cut functions. When storing back into Array1..Array3, you are using indices of the larger array rather than reduced array. POSIX shell and bash happily expand missing entries and fill them with zeros. So bash isn't going to help you find your bug here.

Similarly, the multiply functions should only try to multiply the on the bounds of the reduced-sized arrays. By storing a 2D array as a linear array, getting the side of the array is a bit tricker: either you pass the dimensions as I do below, or you can pick up the size from the array (assuming that's been initialized properly) and take the square root.

You mention what you perceive as random behavior. I suspect what's going on here is that dynamic variable look up from your environment or from past execution is feeding values into the various subroutines. To guard against this, I'd declare all local variables inside the function. Of course there the arrays that aren't local, but again, inside the program you should declare these as well.

That is why I think others have suggested you use a more suitable language for doing something like this. And I agree with those assessments.

But if you do have to use POSIX shell, you probably should know and use it better. You can syntax check your code using bash -n. If the code is POSIX compatible, something I highly recommend here, then ksh -n will give you a more thorough and detailed critique of the program.

To help you find bugs in a program of this, kind, I suggest the bash debugger. And for testing the code, I'd suggest Kate Ward's Unit Test program shunit .

I've rewritten the code fixing the bugs. As best as I can tell it follows what you say you want to do and the code that you have. However you've never really described what you are trying to do, or give the expected answer to the specific data you have so I don't have a way independently check things.

The one thing that I would recommend doing but didn't do below is DRY (Do not Repeat Yourself) the code. The 4 cut functions can be folded into a single routine if you pass start and end points and the name of the array you want to store the result in. I think you'd have to use eval here though.

Likewise the 3 multiply functions can be folded into one by passing in the names of the arrays that get worked on.

I have removed duplicate of the array-showing code by beefing up your ShowMatrix routine.

typeset -ir n=4
typeset -a Array=()
typeset -a Array1=()
typeset -a Array2=()
typeset -a Array3=()
typeset -a Array4=()
typeset -a Result=()
typeset -a Result1=()
typeset -a Result2=()


ShowMatrix() {
    typeset arr=$1
    typeset n=$2
    typeset -i i
    echo "Matrix $arr is:"
    for ((i=0;i<n;i++)) ; do
        typeset -i j
        typeset -i val
        for ((j=0;j<n;j++)) ; do
            ((val=${arr}[i*n+j]))
            printf '%5d ' $val
        done
        printf '\n';
    done
}

cut1() {
    typeset -i i
    typeset -i k=0
    for((i=0;i<n/2;i++)) ; do
        typeset -i j
        for((j=0;j<n/2;j++)); do
            ((Array1[k++] = Array[i*n+j]))
        done
    done
}

cut2() {
    typeset -i i
    typeset -i k=0
    for((i=0;i<n/2;i++)) ; do
        typeset -i j
        for((j=n/2;j<n;j++)) ; do
            ((Array2[k++] = Array[i*n+j]))
        done
    done
}

cut3() {
    typeset -i i
    typeset -i k=0
    for((i=n/2;i<n;i++)) ; do
        for((j=0;j<n/2;j++)) ; do
            ((Array3[k++] = Array[i*n+j]))
        done
    done
}

cut4() {
    typeset -i i
    typeset -i k=0
    for((i=n/2;i<n;i++)) ; do
        for((j=n/2;j<n;j++));  do
           ((Array4[k++] = Array[i*n+j]))
        done
    done
}

multiply() {
    typeset -i i
    typeset -i n=$1
    ShowMatrix Array1 $n
    ShowMatrix Array2 $n
    for((i=0;i<n;i++)); do
        typeset -i j
        for((j=0; j < n; j++)); do
            typeset -i l
            ((l=i*n+j))
            ((Result[l]=0))
            typeset -i k
            for((k=0; k<n; k++)) ; do
                    ((Result[l] += Array1[i*n+k]*Array2[k*n+j]))
            done
        done
    done
}

multiply1()
{
    typeset -i n=$1
    ShowMatrix Result $n
    ShowMatrix Array3 $n
    typeset -i i
    for((i=0; i < n; i++)) ; do
        typeset -i j
        for((j=0; j < n; j++)); do
            typeset -i l
            ((l=i*n+j))
            ((Result1[i*n+j]=0))
            typeset -i k
            for ((k=0;k<n;k++));  do
                ((Result1[l] += Result[i*n+k]*Array3[k*n+j]))
            done
        done
    done
}

multiply2() {
    typeset -i i
    typeset -i n=$1
    ShowMatrix Result1 $n
    ShowMatrix Array4 $n
    for ((i=0; i<n; i++)) ; do
        typeset -i j
        for ((j=0; j < n; j++)) ; do
            typeset -i l
            ((l=i*n+j))
            ((Result2[i*n+j]=0))
            typeset -i k
            for((k=0;k<n;k++)); do
                ((Result2[l] += Result1[i*n+k]*Array4[k*n+j]))
            done
        done
    done
}


typeset -i i
for((i=0; i<n*n; i++)) ; do
       ((Array[i]=i+1))
done

cut1
cut2
cut3
cut4

typeset -i n2
((n2 = n / 2))

multiply $n2
multiply1 $n2
multiply2 $n2
ShowMatrix Result2 $n2
rocky
  • 7,226
  • 3
  • 33
  • 74