Muskie
Muskie

Reputation: 577

Unexpected behaviour when inserting object into NSMutableArray

So I've been working on Project Euler problem 25 in Objective-C and I ran up against the size limit of NSDecimalNumber. So after attempting to use other code libraries I decided to re-represent my Fibonacci number, first as arrays of ints then NSArrays of NSNumbers. Each cell in the array would be one digit. Then I could add to large numbers together digit by digit up to the 1000th digit. Unfortunately something goes wrong, my carry is correct but the digit before the last carry seems to always be zero.

I've tried calculating the 8th Fibonacci number and the 12th Fibonacci number and get the same behaviour. Instead of getting 13 and 144 I get 10 and 104. I sum the digits correctly but

[nextFib insertObject: [NSNumber numberWithInt:digitSum] atIndex: arrayIndex]; 

Doesn't seem to be doing what I expect. digitSum is a 3 and a 4 in both instances, but once my method returns the next fibonacci number as an NSArray I have a 0 where I expected a 3 or a 4. I've tried stepping through it, and I'm baffled. It seems to work some of the time but other times the NSNumber I create on the fly is not having the value I expect. Here is my entire method:

+ (NSArray*) nextBigFibonancci: (NSArray*) fibZero After: (NSArray*) fibOne
{
  // It's come to manually adding digits in one thousand count arrays.
  // I can't return or pass arrays... will have to use NSArrays for everything, version at least 4.0
  // Since XCode 4.5 I can use array[i] and other array literals... Lets just get it working...

  // Works for Fib 1 and Fib 2 and Fib 3, but not Fib 12...

  int fibZeroDigits = [fibZero count];
  int fibOneDigits = [fibOne count];
  NSMutableArray*  nextFib = [NSMutableArray arrayWithCapacity:1000];
  NSArray* number;
  int arrayIndex = 999;
  int cellCount = fibZeroDigits - 1;
  int digitZero, digitOne, digitSum;

  // There is an Objective-c loop structure for looping through all objects in array, but stick to this...
  for (int j = 0; j < 1000; j++)
  {
    // All the integers must be zero.
    [nextFib insertObject: [NSNumber numberWithInt:0] atIndex: j];
  }

  for (int i = fibOneDigits; i > 0; i--)
  {
    digitZero = [[fibZero objectAtIndex: cellCount] intValue];
    digitOne = [[fibOne objectAtIndex: i - 1] intValue];
    NSLog(@"arrayIndex is: %i", arrayIndex); // arrayIndex seems correct why am I getting 104?
    if (digitZero + digitOne < 10)
    {
        digitSum = digitZero + digitOne + [[nextFib objectAtIndex: arrayIndex] intValue];
        [nextFib insertObject: [NSNumber numberWithInt:digitSum] atIndex: arrayIndex];
    }
    else
    {
        digitSum = digitZero + digitOne - 10 + [[nextFib objectAtIndex:arrayIndex] intValue];
        // This isn't working the second time, though digitSum is added correctly...
        // Getting 1,0,4 for fibTwelve instead of 144
        // Doesn't work for fibEight get 1,0 instead of 13...
        [nextFib insertObject: [NSNumber numberWithInt:digitSum] atIndex: arrayIndex]; 
        [nextFib insertObject: [NSNumber numberWithInt: 1] atIndex: arrayIndex -1];
    }
    arrayIndex = arrayIndex - 1;
    cellCount = cellCount - 1;
  }
  // Must carry the last digit in fibOne if arrays are of different sizes...

  if (fibZeroDigits < fibOneDigits)
  {
    // fibOne has one extra digit
    digitSum = [[fibOne objectAtIndex:0] intValue] + [[nextFib objectAtIndex:arrayIndex - 1] intValue];
    [nextFib insertObject:[NSNumber numberWithInt:digitSum] atIndex:arrayIndex -1];
  }

  // Shouldn't return nextFib, but only the signifigant, ie non zero integers

  // Find first non zero digit and then the range from there until the end of the array nextFib
  for(int n = 0; n < 1000; n++)
  {
    if ([[nextFib objectAtIndex: n] intValue] > 0)
    {
        // First non zero digit.
        NSRange theRange;

        theRange.location = n;
        theRange.length = 1000 - n;

        number = [nextFib subarrayWithRange:theRange];
        break; // Could set n = 1000 which would also break...
     }
   }


  return number;
}

Any ideas why digitSum and the NSNumber I create with it isn't getting stored as expected when carrying is necessary?

Upvotes: 0

Views: 243

Answers (2)

Muskie
Muskie

Reputation: 577

As alluded to above, I found my own bug and several more. insertObjectAtIndex adds an entirely new object to the NSMutableArray, I instead wanted to replaceObjectAtIndexWith which replaced the previous digit with the newly calculated digit.

XCode just updated adding the ability to look inside NSArrays from the debugger which would have been nice as I also mentioned above. Here is the method for adding two 1000 digit numbers, it could be modified to add even larger numbers, they don't have to even be Fibonacci numbers.

+ (NSArray*) nextBigFibonancci: (NSArray*) fibZero After: (NSArray*) fibOne
{    
    int fibZeroDigits = [fibZero count];
    int fibOneDigits = [fibOne count];
    int loops = fibZeroDigits - 1;
    int fibOneIndex = loops;
    NSMutableArray*  nextFib = [NSMutableArray arrayWithCapacity:1000];
    NSArray* number;
    int arrayIndex = 999;
    int digitZero, digitOne, digitSum;

    // There is an Objective-c loop structure for looping through all objects in array, but I'll just stick to this...
    for (int j = 0; j <= arrayIndex; j++) 
    {
        // All the integers start at zero.
        [nextFib insertObject: [NSNumber numberWithInt:0] atIndex: j];
    }

    if (fibOneDigits > fibZeroDigits)
    {
        fibOneIndex++;
    }

    for (int i = loops; i >= 0; i--)
    {
        digitZero = [[fibZero objectAtIndex: i ] intValue];
        digitOne = [[fibOne objectAtIndex: fibOneIndex ] intValue];
        // Have to use replaceObjectAtIndex not insertObjectAtIndex!
        digitSum = digitZero + digitOne + [[nextFib objectAtIndex: arrayIndex] intValue];
        if (digitSum < 10)
        {
            [nextFib replaceObjectAtIndex: arrayIndex withObject: [NSNumber numberWithInt:digitSum]];
        }
        else
        {
            digitSum = digitSum - 10;
            [nextFib replaceObjectAtIndex: arrayIndex withObject: [NSNumber numberWithInt:digitSum]];
            [nextFib replaceObjectAtIndex: arrayIndex -1 withObject: [NSNumber numberWithInt:1]];
        }
        arrayIndex = arrayIndex - 1;
        fibOneIndex = fibOneIndex -1;
    }
    // Must carry the last digit in fibOne if arrays are of different sizes...

    if (fibZeroDigits < fibOneDigits)
    {
        // fibOne has one extra digit
        digitSum = [[fibOne objectAtIndex:0] intValue] + [[nextFib objectAtIndex:arrayIndex] intValue];
        [nextFib replaceObjectAtIndex: arrayIndex withObject: [NSNumber numberWithInt:digitSum]];
    }

    // Shouldn't return nextFib, but only the signifigant, ie non zero integers
    // Find first non zero digit and then the range from there until the end of the array nextFib
    for(int n = 0; n < 1000; n++)
    {
        if ([[nextFib objectAtIndex: n] intValue] > 0)
        {
            // First non zero digit.
            NSRange theRange;

            theRange.location = n;
            theRange.length = 1000 - n;  

            number = [nextFib subarrayWithRange:theRange];
            break; // Could set n = 1000 which would also break...
        }
    }


    return number;
}

Upvotes: 1

John Sauer
John Sauer

Reputation: 4421

I also used an NSArray of NSNumbers to solve this. You can use much less code if you store the Fibonacci numbers' digits in reverse order.

I created a method with 2 arguments, both NSArrays representing Fibonacci numbers, and it returns an NSArray representing the next number. So, to figure out the 13th Fibonacci number by adding 89 and 144, the methods two arguments' values are [9, 8] & [4, 4, 1], and it returns [3, 3, 2].

I think you'll find it simpler to 'carry the one' this way. Apparently there's also a way to solve this on paper if you're a genius mathematician.

Upvotes: 0

Related Questions