PDA

View Full Version : Random number issue in Cocoa




bahlquist
Oct 15, 2010, 10:54 PM
Hi,
I made a simple program with a single button, that when pressed gives me a random number. I ran into some issues which several calls of NSLog highlight. Here is my code:

BARandom.h:

#import <Cocoa/Cocoa.h>

@interface BARandom : NSObject {
NSNumber* probability;
}
- (IBAction)button:(id)sender;
@end




BARandom.m:

#import "BARandom.h"
#include <math.h>

@implementation BARandom
- (IBAction)button:(id)sender {
NSNumber* theNumb;
theNumb=[[NSNumber numberWithFloat:rand()] retain];
NSLog(@"%d", [theNumb floatValue]);
NSLog(@"%d", ([theNumb floatValue]+1));
NSLog(@"%d and %d", [theNumb floatValue],[theNumb floatValue]);
}
@end

In the three calls of NSLog, the second number printed is the same as the first, even though it should be 1 greater. The fourth number is different from the rest. In addition, the first three numbers (being the same) only differ by several hundred or so at each press, where as the fourth differs a lot, is sometimes negative, and often repeats.

Any ideas on what is going on?



kpua
Oct 15, 2010, 11:20 PM
%d is for signed integers. For floats, use %f.

bahlquist
Oct 15, 2010, 11:49 PM
%d is for signed integers. For floats, use %f.

Thanks. I feel stupid. I will probably remember it well now.

I still am not getting 1 added to the second output. Tried forcing 1 to be a float by writing (float)1 instead.

I also had trouble when I seeded my random numbers inside the function declaration.

chown33
Oct 16, 2010, 02:06 AM
Post your modified code.

In general, always post code. Descriptions can differ from code. Also, we can't debug descriptions.

bahlquist
Oct 16, 2010, 02:17 AM
Post your modified code.

In general, always post code. Descriptions can differ from code. Also, we can't debug descriptions.

Change "%d" to "%f" everywhere:

BARandom.h:

#import <Cocoa/Cocoa.h>

@interface BARandom : NSObject {
NSNumber* probability;
}
- (IBAction)button:(id)sender;
@end




BARandom.m:

#import "BARandom.h"
#include <math.h>

@implementation BARandom
- (IBAction)button:(id)sender {
NSNumber* theNumb;
theNumb=[[NSNumber numberWithFloat:rand()] retain];
NSLog(@"%f", [theNumb floatValue]);
NSLog(@"%f", ([theNumb floatValue]+1));
NSLog(@"%f and %f", [theNumb floatValue],[theNumb floatValue]);
}
@end


Second number in output is not 1 greater than the first.

bahlquist
Oct 16, 2010, 02:27 AM
Post your modified code.

In general, always post code. Descriptions can differ from code. Also, we can't debug descriptions.

Concerning the random number seeding: seeding inside the function declaration as follows was a problem:

BARandom.h:

#import <Cocoa/Cocoa.h>

@interface BARandom : NSObject {
NSNumber* probability;
}
- (IBAction)button:(id)sender;
@end




BARandom.m:

#import "BARandom.h"
#include <math.h>

@implementation BARandom
- (IBAction)button:(id)sender {
srand(time(0));
NSNumber* theNumb;
theNumb=[[NSNumber numberWithFloat:rand()] retain];
NSLog(@"%d", [theNumb floatValue]);
NSLog(@"%d", ([theNumb floatValue]+1));
NSLog(@"%d and %d", [theNumb floatValue],[theNumb floatValue]);
}
@end


The output numbers were very close to one another. I fixed this problem by moving the "srand(time(0))" line to the "main()" function. Not sure why it didn't work when called as above.

chown33
Oct 16, 2010, 12:37 PM
It didn't work because you were reseeding every time. You were also reseeding with the current time. Since the time advances linearly, you were reseeding with a series of numbers that were very close to one another. Frequent reseeding is never good, but frequent reseeding with non-random values can utterly destroy the randomness of any generator.

I also recommend reading the man page for rand() and srand(), especially this summary at the top of the page:
rand, srand, sranddev, rand_r -- bad random number generator

So another contributing factor is that the functions you're using are a bad random number generator. The random() or arc4random() functions are much better, as noted in the man page for rand().

And your posted code is again using %d when it should be using %f, so it's wrong again.

bahlquist
Oct 16, 2010, 01:18 PM
It didn't work because you were reseeding every time. You were also reseeding with the current time. Since the time advances linearly, you were reseeding with a series of numbers that were very close to one another. Frequent reseeding is never good, but frequent reseeding with non-random values can utterly destroy the randomness of any generator.

I also recommend reading the man page for rand() and srand(), especially this summary at the top of the page:
rand, srand, sranddev, rand_r -- bad random number generator

So another contributing factor is that the functions you're using are a bad random number generator. The random() or arc4random() functions are much better, as noted in the man page for rand().

And your posted code is again using %d when it should be using %f, so it's wrong again.

Thanks. I knew that time was a factor here, but I thought the seeded values, if slightly different, would lead to much different number generations (sensitivity to initial conditions). I guess that's not the case at all and it shows that rand() is not a good random number generator.

Sydde
Oct 16, 2010, 01:31 PM
Be aware, again, that you are leaking theNumb each time you call the method. You should either release it at the end of the method or just not retain it in the first place (a method can almost always rely on an autoreleased object for the length of its run, barring the explicit use of an autorelease pool).

For most math, though, you should probably consider using primitives in place of NSNumber. NSNumber's primary function is as a wrapper for placing numeric values into collections (NSArrays, NSDictionaries, user defaults etc) or perhaps for encoding purposes. Primitives work just fine as local variables as well as ivars and are much faster and easier to work with than NSNumbers.

chown33
Oct 16, 2010, 01:50 PM
Second number in output is not 1 greater than the first.

Yes it is. Sample output:
2010-10-16 11:34:34.722 a.out[5701] 16807.000000 <-- first number
2010-10-16 11:34:34.722 a.out[5701] 16808.000000 <-- second number
2010-10-16 11:34:34.722 a.out[5701] 16807.000000 and 16807.000000

When you perceive a problem in a program's output, post the output. Be prepared to explain why the output has a problem.

When describing a problem, always do the following:
1. Describe exactly what you expected to happen.
2. Describe exactly what did happen.


If you meant "the 4th number isn't 1 greater than the 3rd", then you're right, but it's not supposed to be. Your code is this:
NSLog(@"%d and %d", [theNumb floatValue],[theNumb floatValue]);
That code prints the same value twice, which is exactly what the sample output shows.

If you expected that the previous line of code involving a +1 would increment theNumb's floatValue, then that is very badly mistaken.

So please describe exactly what you expected in the output, and explain why you expected it, and why the sample output is wrong. If you have different output, then post it, because we can't see your screen.

bahlquist
Oct 16, 2010, 07:40 PM
Yes it is. Sample output:
2010-10-16 11:34:34.722 a.out[5701] 16807.000000 <-- first number
2010-10-16 11:34:34.722 a.out[5701] 16808.000000 <-- second number
2010-10-16 11:34:34.722 a.out[5701] 16807.000000 and 16807.000000

When you perceive a problem in a program's output, post the output. Be prepared to explain why the output has a problem.

When describing a problem, always do the following:
1. Describe exactly what you expected to happen.
2. Describe exactly what did happen.


If you meant "the 4th number isn't 1 greater than the 3rd", then you're right, but it's not supposed to be. Your code is this:
NSLog(@"%d and %d", [theNumb floatValue],[theNumb floatValue]);
That code prints the same value twice, which is exactly what the sample output shows.

If you expected that the previous line of code involving a +1 would increment theNumb's floatValue, then that is very badly mistaken.

So please describe exactly what you expected in the output, and explain why you expected it, and why the sample output is wrong. If you have different output, then post it, because we can't see your screen.

I'm not sure where the "a" came from in your output. It seems you were using different code since there is no "a" in the code I posted. I will start a new thread on this issue when I have a chance and I will post my code and output from several trials.

chown33
Oct 16, 2010, 09:10 PM
"a.out" is the name of the executable. It's called a.out because I'm lazy when compiling one-off programs at the command-line. a.out is the default name of the compiler's output.

Look at the output you posted:
2010-10-16 18:32:42.181 Tester[419:10b] 16807.000000 and 16808.000000

Compare to the output I posted:
2010-10-16 11:34:34.722 a.out[5701] 16807.000000

I've hilited the corresponding parts of each output using the same color. It's possible to infer from the different formats of the green bracketed parts that you're running a GUI Cocoa program, and I'm running a GUI-less program. It should also be obvious that I ran the code you posted earlier, and you ran different code posted later.