-4

The script takes only one argument (call it n). It then alternately adds and subtracts each digit of the numbers from 1 through n.

For example:

n = 12

1 - 2 + 3 - 4 + 5 - 6 + 7 - 8 + 9 - 1 + 0 - 1 + 1 - 1 + 2

output: 5

#!/bin/bash
number=$1
result=1
if (( number == 1 || number == 0 )); then
    echo
    echo number: $number
    echo
else
    for (( i = 1; i < number; i++ )); do
        if (( i < 10 )); then
            if (( i % 2 == 0 )); then
                let "result = result + i + 1"
            else
                let "result = result - i - 1"
            fi
        else
            for (( i = 10; i <= number; i++ )); do
                if (( i < 100 && i >= 10)); then
                    let "result = result - i/10 + i%10"
                else
                    let "result = result - i/100 + (i%100)/10 - (i/100)%10"
                fi
            done
        fi
    done

    echo
    echo result: $result
    echo number: $number
    echo
fi

It works fine for ints 0 to 9, but for n>9 it gives the wrong answer.

Please correct my script and explain to me why it's behaving like this.

PS I'm new to bash scripting, so I'd love to see another solution to this puzzle

PPS I'm familiar to java

EDIT

I fixed the code, and it works correct now

#!/bin/bash
number=$1
result=1
if (( number == 1 || number == 0 )); then
    echo
    echo number: $number
    echo
    else
    for (( i = 1; i < number; i++ )); do
        if (( i < 9 )); then
            if (( i % 2 == 0 )); then
                let "result = result + i + 1"
            else
                let "result = result - i - 1"
            fi
        fi
    done
    for (( i = 10; i <= number; i++ )); do
        if (( i < 100 )); then
            let "result = result - i/10 + i%10"
        else
            if (( i % 2 == 0 )); then
                let "result = result - i/100 + (i%100)/10 - (i%100)%10"
            else
                let "result = result + i/100 - (i%100)/10 + (i%100)%10"
            fi
        fi
    done

    echo
    echo result: $result
    echo number: $number
    echo
fi
vixero
  • 514
  • 1
  • 9
  • 21
  • Does this do what you want? What operator precedence do you expect here? `result = result - i - 1` – Brian Cain Sep 03 '14 at 20:15
  • You should be using `i <= number`...which means that the script actually isn't behaving correctly for the correct reason for `0` through `9`. – Kyle Strand Sep 03 '14 at 20:20
  • @KyleStrand yeah, it's true for ints >= 9, I'll edit the code – vixero Sep 03 '14 at 20:28
  • What? No, now you have a nested `for` loop for absolutely no reason. I'm referring to the first `for` line. The ending condition is wrong. – Kyle Strand Sep 03 '14 at 20:32
  • 2
    If a problem hasn't been isolated to the point where you know where bash is doing something you don't expect, it's not ready to ask yet. All you'd need to do to figure out where/how/why that's happening would be some `echo`s. – Charles Duffy Sep 03 '14 at 20:36
  • I suspect, by the way, that you're throwing yourself off by using the variable name `i` twice -- for both inner and outer loops. – Charles Duffy Sep 03 '14 at 20:37
  • @KyleStrand why would it be? if i = 8 then the result will be result + 8 + 1, and that's where it should stop – vixero Sep 03 '14 at 20:53
  • @CharlesDuffy I think the problem is in "result = result - i/10 + i%10", instead of doing the whole thing, it only does result - i. If a do script 9, the result is 5 and for script 10 the result is -5 – vixero Sep 03 '14 at 20:56
  • @CharlesDuffy I've change the 2nd i name, it changes nothing – vixero Sep 03 '14 at 20:59
  • `(( result = result - (i / 10) + (i % 10) ))`, if you want to be entirely clear about precedence. Of course, I don't know why you still have questions about whether that's the line with the bug or not -- if you `echo "$result" "$i"` before that line, and `echo "$result"` after it, you'll be able to see if that's really where the problem is, and if so make this a one-line question, not a 20ish-line one. – Charles Duffy Sep 03 '14 at 21:00
  • I found a small mistake in the 1st if condition.. it should be `(( i < 9 ))`. Now it works for ints 0 to 99. Ty @KyleStrand for giving me the idea about the stop condition. – vixero Sep 03 '14 at 21:21
  • Are you sure? I don't really see how it could possibly work. I'll copy my solution in a new answer. – Kyle Strand Sep 03 '14 at 21:39
  • @KyleStrand you can try my code and see for yourself that it works for 0 to 99 – vixero Sep 03 '14 at 21:43
  • Oh, I see. Your starting condition of `result=1` is really solving the `number=1` case, then each subsequent number `n` is really solving the `number=n+1` case (because of the extra `+1` and `-1`), but stopping short because of the `i < number` condition. This is an insane case of guess-and-check off-by-one debugging. – Kyle Strand Sep 03 '14 at 21:50
  • @KyleStrand otherwise it would add then number after 9 to the result, and it would be `... + 9 - 10` instead of `...+ 9 - 1 + 0`.. Lets say `n=2`, I want to do `1 - 2` and that's the same as `1 - 1 + 1`. Because I'm adding the current int to the next int, it should be `int +/- int + 1`. And if we write `i <= number` then we will add the last number to the next number which is not in our list – vixero Sep 03 '14 at 22:03
  • Okay, first, your order of operations is all wrong; `1 - 2` is the same as `1 - (1 + 1)`, not `1 - 1 + 1`. Second, your explanation doesn't actually make much sense. For the loop iteration when `i = 3`, which *digit* are you handling? (I.e., in the series `1 - 2 + 3 - 4 + 5...`, which operation is the case `i = 3` handling?) – Kyle Strand Sep 03 '14 at 22:22
  • Third, your inner loop is just finishing your outer loop and causing your outer loop to immediately terminate once control returns to it--BUT it's actually going one *further* than the outer loop would go, because you have `i <= number`. This is because the logic for the inner loop doesn't have the extra `+1`/`-1` in it, so it needs to go one further than the outer loop would. – Kyle Strand Sep 03 '14 at 22:26
  • First, I know that `1 - 2` is `1 - 1 - 1`, the code starts at `let "result = result - i - 1"`, I don't see the problem. Second, for `i = 3` it handles `result = result - 3 - 1`. Third, yeah, you're right.. but for some reason it works T_T. I've fixed it – vixero Sep 03 '14 at 22:54
  • I'm sorry, it handles `result + 2 + 1` – vixero Sep 03 '14 at 22:59
  • @Fizunik So what digit does `result + 2 + 1` represent in the series `1 - 2 + 3 - 4 + ...`? The answer is `3`, which seems counter-intuitive, and is probably why you don't understand why your inner loop works correctly. The reason is that above `9`, the code works the "intuitive" way; for example, the case `number = 12` handles the digits `- 1 + 2`, *not* `- 1 + 3`. – Kyle Strand Sep 04 '14 at 16:43

3 Answers3

4

Ok this should work apologies that I miss-read the question earlier

number=$1
MATSTR="";
for((i=1; i <= number; i++)); do
        MATSTR="$MATSTR$i";
done;
echo $MATSTR | sed -e 's/\(.\)\(.\)/\1-\2+/g' | sed 's/+$//' | bc 

Explanation:

  • For loop to create string concatenating all numbers from 1 to the number.
  • sed statement replaces the every two digits with 1st digit minus second digit plus
  • sed removes any trailing plus
  • bc calculates the statement
ShaneQful
  • 2,140
  • 1
  • 16
  • 22
  • However, I believe it gives the wrong answer beginning at `10`. If `10` should be `+0` to make `11=1`, the the answer should be `5` for `i=10` not `4`? – David C. Rankin Sep 03 '14 at 21:50
  • Thanks and 10 should be `1 - 2 + 3 - 4 + 5 - 6 + 7 - 8 + 9 - 1 + 0` which is equal to 4 – ShaneQful Sep 03 '14 at 21:54
  • 1
    Now I see it! Use both digits from `10` not just making `10=0`. You nailed it - the only way to do that is to concatenate all digits and then walk down the string adding and subtracting each. Well done with bc. – David C. Rankin Sep 03 '14 at 22:00
  • @DavidC.Rankin I've fixed my code in the 1st post, it works now. So it's not the only way :P – vixero Sep 03 '14 at 22:05
  • @ShaneQful - Good job with the `bc` based answer. I finally got the logic. The graph this produces is pretty neat. – David C. Rankin Sep 03 '14 at 22:23
  • @Fizunik Your solution won't work for numbers greater than `100`, and generalizing it to work for arbitrarily large numbers would probably not be feasible. Note that this solution already works for arbitrarily large numbers. – Kyle Strand Sep 03 '14 at 22:30
  • @Fizunik - no the string method isn't the **only** solution, but things get really complex for number greater than say 100, or 1000, or 10000 real quick. Without the string approach, you have to add an additional layer of logic each time an additional digit is encountered. Then efficiency come into play. I'll test `bc` solution and loop solution for 10000 and see the difference. – David C. Rankin Sep 03 '14 at 22:33
  • @DavidC.Rankin I assume you'll first generalize the division-followed-by-modulo approach to breaking up the digits so that it will work for arbitrarily large numbers? (Something like `j = i; while (( j > 10 )); do let (( j = j/10 )); done`, I'd assume.) – Kyle Strand Sep 03 '14 at 22:36
  • @DavidC.Rankin Ah, nevermind, you mean *your* loop-based solution. – Kyle Strand Sep 03 '14 at 22:37
  • 1
    The `bc` solution is far more efficient at large numbers than a loop solution. Take 1100 (ans=310). Loop `time => 2.1 sec`, with bc `time => 0.19 sec` – David C. Rankin Sep 03 '14 at 22:37
  • Yeah, I see.. tyvm for helping – vixero Sep 03 '14 at 23:04
  • @ShaneQful - do you really need the second call to `sed`? (`sed 's/+$//'`)? I can't find a situation where the string ends in `+`. If you do need it, you can eliminate a pipe by writing `sed -e 's/\(.\)\(.\)/\1-\2+/g' -e 's/+$//'`, but I think you can get rid of it all together. – David C. Rankin Sep 03 '14 at 23:08
  • @DavidC.Rankin Yep you need the second call for numbers like 8 which produce strings like `1-2+3-4+5-6+7-8+` which would cause issue for `bc`. – ShaneQful Sep 04 '14 at 00:19
  • Gotcha, just checked and got `1-2+3-4+5-6+7-8+` with `n=8` using the first only. I guess it doesn't matter whether you use `/+\1-\2/`, you either have to get rid of it on the front-end or the back-end. – David C. Rankin Sep 04 '14 at 00:48
2

Here's your original solution, but with bug fixes (in comments). It still won't work for number > 99, of course.

#!/bin/bash
number=$1
result=0                                            # Don't start at 1!!
if (( number == 1 || number == 0 )); then
    echo
    echo number: $number
    echo
else
    for (( i = 1; i <= number; i++ )); do           # <=, not <
        if (( i < 10 )); then                       # < 10, not < 9
            if (( i % 2 == 0 )); then
                let "result = result - i"           # subtraction! Also, the +1 was unnecessary
            else
                let "result = result + i"           # addition!
            fi
        else
            if (( i < 100 && i >= 10)); then
                let result=result-i/10+i%10
            else
                let "result=result-i/100+(i%100)/10-(i/100)%10"
            fi
        fi
    done

    echo
    echo result: $result
    echo number: $number
    echo
fi 
Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • @Fizunik I've explained how your code works (more or less accidentally, it seems). I've also fixed it so that it works in a reasonable, mostly-readable, and semantically-correct manner. Why are you assuming it doesn't work rather than trying it out? – Kyle Strand Sep 03 '14 at 22:32
  • Yeah, sorry.. I've copy pasted it, but didn't save it when I was testing it. My bad. I see your point there. – vixero Sep 03 '14 at 22:39
1

After misreading the question, (or the question being unclear), the answer took only minor adjustments. The sting solution is probably the simplest approach using modulo to togget +/-:

#!/bin/bash

declare -i num=$1
declare -i res=0
str=""

[ "$num" -gt 1 ] || {
    printf "\n Error: invalid input. usage: %s int (greater than 1)\n\n" "${0//*\//}"
    exit 1
}

for ((i = 1; i <= $num; i++)); do    # create the string of digits
    str="${str}${i}"
done

printf "\n Calculations:\n\n"

for ((i = 0; i < ${#str}; i++)); do  # walk down the string adding/subtracting each digit
    if [ $((i % 2)) -eq 0 ]; then
        ((res+=${str:$i:1}))
        printf "  res + %3s = %3s\n" "${str:$i:1}" "$res"
    else
        ((res-=${str:$i:1}))
        printf "  res - %3s = %3s\n" "${str:$i:1}" "$res"
    fi
done

printf "\n Final Result: %s\n\n" "$res"

exit 0

output:

$ ./puzzle.sh 12

Calculations:

  res +   1 =   1
  res -   2 =  -1
  res +   3 =   2
  res -   4 =  -2
  res +   5 =   3
  res -   6 =  -3
  res +   7 =   4
  res -   8 =  -4
  res +   9 =   5
  res -   1 =   4
  res +   0 =   4
  res -   1 =   3
  res +   1 =   4
  res -   1 =   3
  res +   2 =   5

Final Result: 5
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85