Create a non-repeating random number [duplicate] - ios

This question already has answers here:
Non repeating random numbers in Objective-C
(6 answers)
Closed 8 years ago.
I would like to know how to make it so that the same picture is not chosen twice in a row. Lets say pictures are given a number 1-3. If picture 1 is chosen, then picture 1 will not be chosen next. If picture 3 is chosen, then picture 1 can be chosen again, and so on.
I know that I would have to use a while statement, except I don't know how. Heres what I have as of now:
- (void)chooseBackgroundImage{
if(thisNumber % 10 == 0){
int chooseBackgroundImage = arc4random() % 7;
switch (chooseBackgroundImage) {
case 0:
backgroundImage.image = [UIImage imageNamed:#"CyanToYellowBackground.png"];
break;
case 1:
backgroundImage.image = [UIImage imageNamed:#"GreenToBlueBackground.png"];
break;
case 2:
backgroundImage.image = [UIImage imageNamed:#"OrangeToGreenBackground.png"];
break;
case 3:
backgroundImage.image = [UIImage imageNamed:#"OrangeToPurpleBackground.png"];
break;
case 4:
backgroundImage.image = [UIImage imageNamed:#"PurpleToCyanBackground.png"];
break;
case 5:
backgroundImage.image = [UIImage imageNamed:#"RedToBlueBackground.png"];
break;
case 6:
backgroundImage.image = [UIImage imageNamed:#"YellowToRedBackground.png"];
break;
}
}
}
I've also tried using:
- (void)chooseBackgroundImage{
if(slogansGenerated % 10 == 0){
int chooseBackgroundImage = arc4random() % 7;
while(chooseBackgroundImage == oldChooseBackgroundImage){
switch (chooseBackgroundImage) {
case 0:
backgroundImage.image = [UIImage imageNamed:#"CyanToYellowBackground.png"];
break;
case 1:
backgroundImage.image = [UIImage imageNamed:#"GreenToBlueBackground.png"];
break;
case 2:
backgroundImage.image = [UIImage imageNamed:#"OrangeToGreenBackground.png"];
break;
case 3:
backgroundImage.image = [UIImage imageNamed:#"OrangeToPurpleBackground.png"];
break;
case 4:
backgroundImage.image = [UIImage imageNamed:#"PurpleToCyanBackground.png"];
break;
case 5:
backgroundImage.image = [UIImage imageNamed:#"RedToBlueBackground.png"];
break;
case 6:
backgroundImage.image = [UIImage imageNamed:#"YellowToRedBackground.png"];
break;
}
int oldChooseBackgroundImage = chooseBackroundImage
}
}
But nothing seems to work. Is there any way to create a non-repeating random number?

The following is probably random enough for you needs:
First add an instance variable, say, lastChosenBackgroundImage.
After:
int chooseBackgroundImage = arc4random() % 7;
add:
if(chooseBackgroundImage == lastChosenBackgroundImage)
chooseBackgroundImage = (chooseBackgroundImage + 1) % 7; // same as last time, move to next choice
lastChosenBackgroundImage = chooseBackgroundImage; // remember for next time
This does mean that picking the next image is twice as probable as picking any of the other ones, but I suspect that will not be a significant issue for your use case.

I recon you're looking for something like this.
-(int)getNonRepeatedRandom{
int randomNumber = -1;
do{
randomNumber = arc4random_uniform(7);
} while (randomNumber == oldRandomNumber);
oldRandomNumber = randomNumber; //set the random number to old random so you can check it on the next run.
return randomNumber;
}
oldRandomNumber will have to be an iVar for this to work though.
You should use arc4random_uniform instead of arc4random modulus to get rid of modulous bias.

Use this utility function to give you random integers between a range (in your case 0 and 6) -
#define MAX_ATTEMPTCOUNT 10
// Provides a random number between the range (both inclusive).
+ (int)randomIntegerInRange:(int)fromInt toInteger:(int)toInt excluding:(NSArray *)excludeNumbers {
NSAssert((toInt - fromInt) > 0 && (!excludeNumbers ? YES : (toInt - (fromInt - 1)) >= [excludeNumbers count]), #"Invalid range");
static int randomAttempts = 0;
srandom(time(NULL));
int randomInteger = fromInt + random() % (toInt - (fromInt - 1));
if (excludeNumbers != nil) {
for (NSNumber *number in excludeNumbers) {
if ([number intValue] == randomInteger) {
if (randomAttempts == MAX_ATTEMPTCOUNT) {
// Reached the maximum attempt count to get the random number but failed to find one.
break;
}
else {
// Recursive call to get obtain the next number.
++randomAttempts;
randomInteger = [self randomIntegerInRange:fromInt toInteger:toInt excluding:excludeNumbers];
}
break;
}
}
if (randomAttempts >= MAX_ATTEMPTCOUNT) {
// Pick up the first number that's not there in visited words.
randomAttempts = 0; // Reset the counter for next attempt.
randomInteger = fromInt;
for (; randomInteger <= toInt; ++randomInteger) {
bool found = NO;
for (NSNumber *number in excludeNumbers) {
if ([number intValue] == randomInteger) {
// Found the number.
found = YES;
break;
}
}
if (!found) break;
else continue;
}
}
}
return randomInteger;
}
Add the returned integer in an array (array of excluded numbers) which is actually passed as parameter (excludeNumbers).
Let me know if any issue (I made it for one of my project so may not completely suit your need so feel free to modify it or ask me)

Related

If statement inside a switch case in Objective-c

I know there are a couple of questions like this on stack overflow but the problem is that it does not solve my answer.
I want to show an if-statement inside a switch case.
Here is my code
NSMutableArray *stdMarks = [[NSMutableArray alloc]initWithObjects:#"100", #"70", #"97", #"11", #"59", nil];
int marksObtained;
for (int i= 0; i < [stdMarks count]; i++) {
marksObtained += [[stdMarks objectAtIndex:i] integerValue];
}
int totalMarks = 500;
int percentage = (marksObtained * 100)/totalMarks;
NSLog(#"The total marks are: %d", marksObtained);
NSLog(#"The percentage is: %d", percentage);
NSLog(#"The total numbers of subjects are: %d", [stdMarks count]);
switch (percentage) {
case 1:
if (percentage >= 70) {
NSLog(#"You get 50 percent fee concession");
}
break;
case 2:
if (percentage >= 60) {
NSLog(#"You get 45 percent fee concession");
}
break;
default:
NSLog(#"you do not qualify");
break;
}
No matter what the percentage is I always get the default answer "You do not qualify".
What am I doing wrong
Please help me out here
that is because the switch is being fed the percentage which only caters for 1 and 2 percent, so those if statements will never fire because to get into those cases, they have to be lower then the values in the if statements
my suggestion is to drop the switch statement, because it seems unnecessary, just have if/else statements that encompass the ranges you want
In your code, if your percentage is not equal to run 1 or 2, it will go to default.
switch (percentage) { <---- this percentage, you only provide the case of 1 and 2.
case 1:
if (percentage >= 70) {
NSLog(#"You get 50 percent fee concession");
}
break;
case 2:
if (percentage >= 60) {
NSLog(#"You get 45 percent fee concession");
}
break;
default:
NSLog(#"you do not qualify");
break;
}
guessing you need to change the percentage in switch(percentage).
Updated:
if (percentage >= 70) {
NSLog(#"You get 50 percent fee concession");
}
else if (percentage >= 60) {
NSLog(#"You get 45 percent fee concession");
}
else{
NSLog(#"you do not qualify");
}
update:
if you want to do it using switch case:
for percentage >= 70
switch (percentage)
case 70:
case 71:
case 72:
case 73:
.
.
.
case 100:
NSLog(#"You get 50 percent fee concession");
break;
case 60:
.
.
.
and so on which is very stupid , so a if else case is more suitable

How to determine Zodiac Sign in Objective-C

I have the following code:
NSDate *myDate = [datePickerControl date];
NSDateFormatter *format = [[NSDateFormatter alloc]init];
[format setDateFormat:#"dd-MM"];
NSString *getDate = [format stringFromDate:myDate];
NSArray *array = [NSArray arrayWithObjects:#"15-06",#"15-07",#"15-08",nil];
for (int i = 0; i < ([array count]); i++) {
NSLog(#"i = %i", i);
NSString *stringToCheck = (NSString *)[array objectAtIndex:i];
if ([getDate isEqualToString:stringToCheck]) {
[signWow setText:[NSString stringWithFormat:#"Your sign is Scorpion"]];
}}
It uses Date Picker to determine date of birth and then to compare the date with a date from NSArray. But it's too difficult to type in all the dates related to specific Zodiac sign.
So, is it possible to make an array which contains all dates between for example 02/12 (dd/MM) and 03/15?
Take a look at screenshot here.
Please, give some code, because I'm new to Objective-C... :)
How about using tuple... (Swift)
func getZodiacSign(_ date:Date) -> String{
let calendar = Calendar.current
let d = calendar.component(.day, from: date)
let m = calendar.component(.month, from: date)
switch (d,m) {
case (21...31,1),(1...19,2):
return "aquarius"
case (20...29,2),(1...20,3):
return "pisces"
case (21...31,3),(1...20,4):
return "aries"
case (21...30,4),(1...21,5):
return "taurus"
case (22...31,5),(1...21,6):
return "gemini"
case (22...30,6),(1...22,7):
return "cancer"
case (23...31,7),(1...22,8):
return "leo"
case (23...31,8),(1...23,9):
return "virgo"
case (24...30,9),(1...23,10):
return "libra"
case (24...31,10),(1...22,11):
return "scorpio"
case (23...30,11),(1...21,12):
return "sagittarius"
default:
return "capricorn"
}
}
You should not need to enter every date for each sign. Instead, enter a table of the starting and ending month and day for each sign.
You would then take the user's selected date, use a Gregorian NSCalendar to extract the month and day units from the date, and then compare the user-entered date to the ranges for each sign.
BTW, it's "Scorpio", not "Scorpion".
Here's the most straightforward way to do it. Yes it's long, and yes it's not the most efficient, but it is definitely the easiest to understand, and it's already written for you. Cheers :)
P.S. This is going off of the Tropical zodiac (the standard, as opposed to the Sidereal zodiac based off of the Hindu system), and in response to Zev, Ophiuchus only affected each sign's corresponding constellation, but the sign's and their dates remained unaffected.
Here's the code:
-(void)getZodiacFromBirthday {
NSString *UserBirthday = #"09/09/99";
NSArray *birthArray = [UserBirthday componentsSeparatedByString:#"/"];
NSString *month = birthArray[0];
NSString *day = birthArray[1];
if ([month isEqualToString:#"01"]) {
if ([day intValue] >= 21) {
sign = #"Aquarius";
} else {
sign = #"Capricorn";
}
} else if ([month isEqualToString:#"02"]) {
if ([day intValue] >= 20) {
sign = #"Pisces";
} else {
sign = #"Aquarius";
}
} else if ([month isEqualToString:#"03"]) {
if ([day intValue] >= 21) {
sign = #"Aries";
} else {
sign = #"Pisces";
}
} else if ([month isEqualToString:#"04"]) {
if ([day intValue] >= 21) {
sign = #"Taurus";
} else {
sign = #"Aries";
}
} else if ([month isEqualToString:#"05"]) {
if ([day intValue] >= 22) {
sign = #"Gemini";
} else {
sign = #"Taurus";
}
} else if ([month isEqualToString:#"06"]) {
if ([day intValue] >= 22) {
sign = #"Cancer";
} else {
sign = #"Gemini";
}
} else if ([month isEqualToString:#"07"]) {
if ([day intValue] >= 23) {
sign = #"Leo";
} else {
sign = #"Cancer";
}
} else if ([month isEqualToString:#"08"]) {
if ([day intValue] >= 23) {
sign = #"Virgo";
} else {
sign = #"Leo";
}
} else if ([month isEqualToString:#"09"]) {
if ([day intValue] >= 24) {
sign = #"Libra";
} else {
sign = #"Virgo";
}
} else if ([month isEqualToString:#"10"]) {
if ([day intValue] >= 24) {
sign = #"Scorpio";
} else {
sign = #"Libra";
}
} else if ([month isEqualToString:#"11"]) {
if ([day intValue] >= 23) {
sign = #"Sagittarius";
} else {
sign = #"Scorpio";
}
} else if ([month isEqualToString:#"12"]) {
if ([day intValue] >= 22) {
sign = #"Capricorn";
} else {
sign = #"Sagittarius";
}
}
NSLog(#"Sign: %#", sign);
}
For those using swift, i must thanks Duncan C. his solution helped me a lot:
in case that date is "dd/MM/yyyy"
func getZodiacalSign(date:String) -> String {
let f = date.components(separatedBy: "/")
let day = Int(f[0])
let month = Int(f[1])
switch month {
case 1: return (day! >= 21) ? "Aquarius" : "Capricorn";
case 2: return (day! >= 20) ? "Picis" : "Aquarius";
case 3: return (day! >= 21) ? "Aries" : "Pisces";
case 4: return (day! >= 21) ? "Taurus" : "Aries";
case 5: return (day! >= 22) ? "Gemini" : "Taurus"
case 6: return (day! >= 22) ? "Cancer" : "Gemini";
case 7: return (day! >= 23) ? "Leo" : "Cancer";
case 8: return (day! >= 23) ? "Virgo" : "Leo";
case 9: return (day! >= 24) ? "Libra" : "Virgo";
case 10: return (day! >= 24) ? "Scorpio" : "Libra";
case 11: return (day! >= 23) ? "Sagittarius" : "Scorpio";
case 12: return (day! >= 22) ? "Capricorn" : "Sagittarius";
default: return ""
}
Duncan is right, I was writing you a sample code when he answered the question.....
//get date from your picker
NSDate *myDate = [datePickerControl date];
NSCalendar *gregorianCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComps = [gregorianCal components: (NSDayCalendarUnit | NSMonthCalendarUnit)
fromDate: myDate];
// Then use it
int month=[dateComps month];
int days=[dateComps day];
//I guess there is only two zodiac signs possilble for each month right?
switch (month)
{
case 1:
//compare the dates
if (days<=20)
{
//user is Capricorn
}
else
{
//user is Picses
}
break;
case 2:
break;
//you will have 12 cases and each case will have an if else with corect dates.....
default;
break;
}
Here is an answer for those using Swift. It is based upon Hendra Kusumah's tuple solution, but uses extensions and enums for a neater and more Swift-like answer.
Usage
let date = Date()
date.zodiacSign // Enum value e.g. .aquarius
date.zodiacSign.rawValue // String value e.g. "aquarius"
Note - If you wanted the string value to be capitalised or to appear different in another way, either manipulate the string using date.zodiacSign.rawValue.capitalized or edit the ZodiacSign enum cases e.g. case aquarius = "Aquarius"
Extension
extension Date {
var zodiacSign: ZodiacSign {
get {
let calendar = Calendar.current
let day = calendar.component(.day, from: self)
let month = calendar.component(.month, from: self)
switch (day, month) {
case (21...31, 1), (1...19, 2):
return .aquarius
case (20...29, 2), (1...20, 3):
return .pisces
case (21...31, 3), (1...20, 4):
return .aries
case (21...30, 4), (1...21, 5):
return .taurus
case (22...31, 5), (1...21, 6):
return .gemini
case (22...30, 6), (1...22, 7):
return .cancer
case (23...31, 7), (1...22, 8):
return .leo
case (23...31, 8), (1...23, 9):
return .virgo
case (24...30, 9), (1...23, 10):
return .libra
case (24...31, 10), (1...22, 11):
return .scorpio
case (23...30, 11), (1...21, 12):
return .sagittarius
default:
return .capricorn
}
}
}
}
Enum
enum ZodiacSign: String {
case aries
case taurus
case gemini
case cancer
case leo
case virgo
case libra
case scorpio
case sagittarius
case capricorn
case aquarius
case pisces
}

Get an array of 3 ordered values whilst favouring a specified value

Sorry for the somewhat generic title, if anyone has a better suggestion please let me know.
Basically I am writing a custom leaderboard view whereby I want to show 3 scores only. If possible it will show the current user's score in the middle but, if the user is at the top or the bottom of the list, it should still show 3 scores but itll show another users above or below the list.
e.g.
Me (If I am top, then show 2 below)
User 1
User 2
or
User 1
Me (usual case where I am in the middle of two scores)
User 2
or
User 1
User 2
Me (If I am bottom show two scores above me)
I have a function written that does the first part of this but doesnt take into account edge cases which is what I am struggling with. Can anyone please advise?
-(void)getNearbyScores:(int)score{
GCLeaderboardScore *closestScoreAbove = nil; //Custom container for GC properties
GCLeaderboardScore *closestScoreBelow = nil; //Contains playerID, score, alias etc
if ([playerScores count] == 0){ //playerScores is an NSMutableDictionary
return;
}
for (NSString* key in playerScores) {
GCLeaderboardScore *playerScore = (GCLeaderboardScore *)[playerScores objectForKey:key];
if ((closestScoreAbove == nil || closestScoreAbove->score > playerScore->score) && playerScore->score > score){
closestScoreAbove = playerScore;
}
else if ((closestScoreBelow == nil || closestScoreAbove->score < playerScore->score) && playerScore->score < score){
closestScoreBelow = playerScore;
}
}
me->score = score;
me->rank = 1;
if (closestScoreAbove != nil) {
me->rank = closestScoreAbove->rank + 1;
nearbyScores = [NSMutableArray arrayWithObjects: closestScoreAbove, me, closestScoreBelow, nil];
}
else {
nearbyScores = [NSMutableArray arrayWithObjects: me, closestScoreBelow, nil];
}
}
Assuming there is a me GCLeaderboardScore object, the method below should return an array with the desired GCLeaderboardScore objects (untested):
-(NSArray *)getNearbyScores {
if(playerScores.count==0) return nil;
// Create an array sorted by score
NSArray *sortedByScore=[playerScores sortedArrayUsingComparator: ^(id object1, id object2) {
GCLeaderboardScore *score1=object1;
GCLeaderboardScore *score2=object2;
if(score1->score < score2->score) return NSOrderedAscending;
if(score1->score > score2->score) return NSOrderedDescending;
return NSOrderedSame;
}];
// Find index of me
NSUInteger idx=[sortedByScore indexOfObject:me];
// If me not found, return nil
if(idx==NSNotFound) return nil;
// Ideally we want to show the player before and behind
idx=MAX(0,(NSInteger)idx-1);
// maxIdx will be idx+2 or index of last object if lower
NSUInteger maxIdx=MIN(sortedByScore.count-1,idx+2);
// In case we are last, show two previous results (if array large enough)
if (maxIdx > 3)
idx=MAX(0,maxIdx-3);
// And return the objects, may be 1..3 objects
return [sortedByScore subarrayWithRange:NSMakeRange(idx,maxIdx-idx+1)];
}
I assume you have an array of scores. (Actual implementation can be adapted to your code)
Initialize:
firstScoreAbove = VERY_LARGE_SCORE; secondScoreAbove = VERY_LARGE_SCORE + 1;
firstScoreBelow = -1; secondScoreBelow = -2;
Scan the array elements.
if ( newScore > myScore ) {
if ( newScore < firstScoreAbove ) {
secondScoreAbove = firstScoreAbove;
firstScoreAbove = newScore;
} else if ( newScore < secondScoreAbove ) {
secondScoreAbove = newScore;
}
} else {
// similarly for below.
}
After scanning,
If firstScoreAbove has not changed, then myScore is top and output the two below scores.
If firstScoreBelow has not changed, then myScore is lowest and output the two above scores.
Else Output firstScoreAbove, myScore, firstScoreBelow.

iOS Fastest method to extract a substring

I have several thousand strings in the form "verb-noun" for which I want to extract the noun portion. I am looking for the FASTEST way to do this. The verb and noun portions can be any length.
I have tried
NSString *noun = [[verb componentsSeparatedByString:#"-"] lastObject];
Which is slow... For my list of over 3000 entries it takes about 3 seconds.
Also tried
NSString *noun = [verb substringFromIndex:[verb rangeOfString:#"-"].location + 1];
which is MUCH faster, about a half second.
Anyone have suggestions for making this even faster?
If your work with these strings is thread-safe then one option is to use GCD to iterate over multiple verb values simultaneously, bringing multiple cores into action. Use dispatch_apply instead of whatever loop you're using, something like:
dispatch_apply([myWordArray count], queue, ^(size_t i) {
NSString *verb = [myWordArray objectAtIndex:i];
NSString *noun = [verb substringFromIndex:[verb rangeOfString:#"-"].location + 1];
// do something with noun...
});
Just keep in mind that this will do more than one pass simultaneously, so be very sure about threading issues.
Fastest way would probably to sort the most likely cases for where the hyphen is, and then check for those first without using a loop. For example, if the most likely cases for index of hyphen are 5, 4, 6, 7, 3, 2 in that order, you could do this:
NSString * verb = #"verb-noun";
NSString * noun = nil;
//use do...while(0) to avoid nested if else
do
{
if([verb characterAtIndex:5] == '-')
{
noun = [verb substringFromIndex:6];
break;
}
if([verb characterAtIndex:4] == '-')
{
noun = [verb substringFromIndex:5];
break;
}
if([verb characterAtIndex:6] == '-')
{
noun = [verb substringFromIndex:7];
break;
}
if([verb characterAtIndex:7] == '-')
{
noun = [verb substringFromIndex:8];
break;
}
if([verb characterAtIndex:3] == '-')
{
noun = [verb substringFromIndex:4];
break;
}
if([verb characterAtIndex:2] == '-')
{
noun = [verb substringFromIndex:4];
break;
}
} while(0);
//if not one of most likely cases, loop
if(!noun)
{
for(int j = 8; j < verb.length; j++)
{
if([verb characterAtIndex:j] == '-')
{
noun = [verb substringFromIndex:j + 1];
break;
}
}
}
if(noun)
{
//noun should never be nil
NSLog(#"found the noun");
}

making sure that every new position is different from previous two positions

Below is a method I wrote that takes a random number and makes sure that a sprite does repeat consecutively at the same position. I want to change it so that every new sprite takes a different position of the other two. I am not really getting it right. Please help.
- (float)randomlyChooseXValue {
CGSize s = [[CCDirector sharedDirector] winSize];
int randX = arc4random() % 3;
if (oldRandX != randX) {
if (randX == 0) {
xPos = xPos1*(s.width/480.0);
} else if (randX == 1) {
xPos = xPos2*(s.width/480.0);
} else {
xPos = xPos3*(s.width/480.0);
}
oldRandX = randX;
} else {
[self randomlyChooseXValue];
}
return xPos;
}
If I understand this correctly you need to find 3 random values and the 3rd one should be different then the 1st 2. If this is true you need to store last 2 random values and compare them in the 1st if statement:
- (float)randomlyChooseXValue {
CGSize s = [[CCDirector sharedDirector] winSize];
int randX = arc4random() % 3;
if ((oldRandX1 != randX) && (oldRandX2 != randX)) { //check for 2 values
if (randX == 0) {
xPos = xPos1*(s.width/480.0);
} else if (randX == 1) {
xPos = xPos2*(s.width/480.0);
} else {
xPos = xPos3*(s.width/480.0);
}
oldRandX2 = oldRandX1; //store 1st value to 2nd place
oldRandX1 = randX; //store new value to 1st place
} else {
[self randomlyChooseXValue];
}
return xPos;
}
Since you explained that you are okay with the sequence repeating, then you only need to really make two random choices: the first position, and the direction.
// somewhere, initialize global oldRandX = -1, dir = -1
- (float)randomlyChooseXValue {
CGSize s = [[CCDirector sharedDirector] winSize];
if (oldRandX < 0) {
oldRandX = arc4random() % 3;
dir = arc4random() % 2;
} else if (dir) {
oldRandX = (oldRandX + 1) % 3;
} else {
oldRandX = (oldRandX + 2) % 3;
}
if (randX == 0) {
xPos = xPos1*(s.width/480.0);
} else if (randX == 1) {
xPos = xPos2*(s.width/480.0);
} else {
xPos = xPos3*(s.width/480.0);
}
return xPos;
}
This can generate every possible sequence of the three positions:
0, 1, 2
0, 2, 1
1, 2, 0
1, 0, 2
2, 0, 1
2, 1, 0
and repeat them.
The other answer will achieve the same results, but like your original method, it might take several tries to get it. Your random function can keep choosing the wrong value an unbounded number of times; it becomes a worse problem when there's only one right number to pick.

Resources