Swift rand() not being random - random

Today is my first day with Swift, and I have run into a problem. I am using rand to generate a random number, but it is giving me the same results every time I run the code.
main.swift:
import Foundation
var player = Player()
for _ in 1..6 {
println(player.kick())
}
player.swift:
import Foundation
class Player {
var health = 25
var xp = 15
var upgrades = ["kick": 0, "punch": 0]
func kick() -> Int {
let range = (3, 7)
let damage = Int(rand()) % (range.1 - range.0) + range.0 + 1
return damage
}
func punch() -> Int {
let range = (4, 6)
let damage = Int(rand()) % (range.1 - range.0) + range.0 + 1
return damage
}
}
Every time I run the code, it logs these numbers:
7
5
5
6
6
I also tried this: Int(arc4random(range.1 - range.0)) + range.0 + 1 but it said it couldn't find an overload for + that accepts the supplied arguments
I have no idea why this would be happening. I'd appreciate some help, thanks!

You should never use rand(), use arc4random - it's a much better generator. If you check its man-pages, you'll find that it has an integer range generator form called arc4random_uniform(), which you should use to avoid modulo bias when the modulus is not a power of 2. I believe the following is what you want, it worked for me in playground:
let damage = arc4random_uniform(UInt32(range.1 - range.0) + 1) + UInt32(range.0)
The + 1 is because the upper end of arc4random_uniform() is non-inclusive. If your range is (4,7), this should give occurrences of 4, 5, 6, and 7.

rand() in most programming environments gives you a repeatable sequence of pseudo-random numbers, by design. Look for a function called seed or srand for ways to initialize the random number generator.

Using rand() is fine, you can seed the pseudo-random number generator with this call at the beginning of your program:
srand(UInt32(time(nil)))

Related

A simple Increasing Mathematical Algorithm

I actually tried to search this, I'm sure this basic algorithm is everywhere on internet, CS textbooks etc, but I cannot find the right words to search it.
What I want from this algorithm to do is write "A" and "B" with the limit always increasing by 2. Like I want it to write A 3 times, then B 5 times, then A 7 times, then B 9 times and so on. And I plan to have 100 elements in total.
Like: AAABBBBBAAAAAAABBBBBBBBB...
I only want to use a single "for loop" for the entire 100 elements starting from 1 to 100. And just direct/sort "A" and "B" through "if/else if/ else".
I'm just asking for the basic mathematical algorithm behind it, showing it through any programming language would be better or redirecting me to such topic would also be fine.
You can do something like this:
There might be shorter answers, but I find this one easy to understand.
Basically, you keep a bool variable that will tell you if it's A's turn or Bs. Then we keep a variable switch that will tell us when we should switch between them. times is being updated with the repeated times we need to print the next character.
A_B = true
times = 3 // 3,5,7,9,...
switch = 3 // 3,8,15,24,...
for (i from 1 to 100)
if (A_B)
print 'A'
else
print 'B'
if (i == switch)
times += 2
switch += times
A_B = !A_B
Python:
for n in range(1, 101):
print "BA"[(int(sqrt(n)) % 2)],
The parity of the square roots of the integers follows that pattern. (Think that (n+1)²-n² = 2n+1.)
If you prefer to avoid the square root, it suffices to use an extra variable that represents the integer square root and keep it updated
r= 1
for n in range(1, 101):
if r * r <= n:
r+= 1
print "AB"[r % 2],
Here is the snippet you can test on this page. It is an example for about 500 letters totally, sure you can modify it for 100 letters. It is quite flexible that you can change the constants to produce lot of different strings in the same manner.
var toRepeat = ['A', 'B'];
var result='', j, i=3;
var sum=i;
var counter = 0;
while (sum < 500) {
j = counter % 2;
result = result + toRepeat[j].repeat(i);
sum = sum + i;
i = i + 2;
counter++;
}
document.getElementById('hLetters').innerHTML=result;
console.log(result);
<div id="hLetters"></div>
If you want it to be exactly 500 / 100 letters, just use a substring function to trim off the extra letters from the end.
To get 100 groups of A and B with increasing length of 3, 5, 7 and so on, you can run this Python code:
''.join(('B' if i % 2 else 'A') * (2 * i + 3) for i in range(100))
The output is a string of 10200 characters.
If you want the output to have only 100 characters, you can use:
import math
''.join(('B' if math.ceil(math.sqrt(i)) % 2 else 'A') for i in range(2, 102))
In js you can start with somethink like this :
$res ="";
count2 = 0;
for (i=2;i<100; i = i+2) {
count = 0;
alert(i);
while (count < i ) {
$res = $res.concat(String.fromCharCode(65+count2));
count++;
}
count2++;
}
alert ($res);

Swift - random number (min - max) avoiding repetition

I have a function that generate an integer random number between two given values. This function in my app will be called only two times however, I want to avoid that it generates two same numbers or two number next to each other. How could I fix the function below to achieve that?
Example Result:
1, 1 = wrong
1, 2 = wrong
8, 7 = wrong
8, 12 = correct
1, 3 = correct
3, 0 = correct
Function:
func randomNumber(minX:UInt32, maxX:UInt32) -> Int {
let result = (arc4random() % (maxX - minX + 1)) + minX
return Int(result)
}
EDIT:
How can it be a duplicate???
I am not asking to shuffle an array but I want to generate a single Int from two given numbers
You said "I want to avoid ... two numbers next to each other" so I don't understand how 8 ,7 is wrong but 8, 9 is correct.
Anyhow, my answer answer is based on your example. Just send the previous value and loop until you get a satisfactory asnwer:
func randomNumber(minX:UInt32, maxX:UInt32, previousNumber: Int? = nil) -> Int {
var result: Int
repeat {
result = Int((arc4random() % (maxX - minX + 1)) + minX)
} while previousNumber == result || previousNumber == result + 1
return result
}
let r1 = randomNumber(1, maxX: 3)
let r2 = randomNumber(1, maxX: 3, previousNumber: r1)
This will lead to an infinite loop when there's no number that can satisfy your conditions, for example:
randonNumber(1, maxX: 2, previousNumber: 2)
You should add a guard statement by working out the possibility mathematically and create a counter so that the repeat loop ends after, say 100 iterations.
Make global/static variable, where you will remember last valid generated number. Then add if statement, which will recursively call randomNumber, when you generate same(/next to each other) number as in that variable. If there hasn't been generated any number, set number out of interval.

Swift random number problems

I am using Swift with Xcode 6 Beta 5, and trying to generate a random number between my specific constraints.
Currently I am using the arc4random_uniform() function, which works for simple generating like:
arc4random_uniform(5)
But I am trying to create a random number with some complicated boundaries. This is my attempt:
randomMovement = Int( arc4random_uniform( (0.25 * Int(cs)) - ( 0.175 * rd ) ) ) + Int( 0.175 * rd )
cs and rd are both integers - initially, cs = 100, and rd = 40. This should generate a random number between 7 and 25. However, I run into many problems in Xcode with this, due to the variable types.
With this code, it initially shows the one error - On the first '*', it says:
'Int' is not convertible to 'UInt8'
But when I change that, it comes up with more problems, and it seems like I keep going round in a circle.
What's going wrong, and how can i fix this?
When you have type problems, build up your expression step by step.
import Darwin
let cs = 100
let rd = 40
let min = UInt32(0.175 * Float(rd))
let max = UInt32(0.25 * Float(cs))
// This isn't really necessary, the next line will crash if it's not true, but
// just calling out this implicit assumption
assert(max >= min)
let randomMovement = Int(arc4random_uniform(max - min) + min)
arc4random_uniform takes a UInt32, so you need to get there. You can't multiply Float and Int without a typecast, so you need to add those.
When I say "build up step-by-step" I mean for you, not the compiler. The compiler can handle:
let randomMovement = Int(arc4random_uniform(UInt32(0.25 * Float(cs)) - UInt32(0.175 * Float(rd))) + UInt32(0.25 * Float(cs)))
but it's a bit hard to understand (and may compute cs/4 twice).
You can use the built in random() function in swift:
let randomInt = random() % 5
in Swift 4.2 you can make something like:
let number = Int.random(in: 0 ..< 10)
With Swift 4.2, you can now call directly from Int.random. You can see more details here.
let min = a
let max = b
var x = min + random_uniform(max - min)

Given a random number generator, using it to make a new one

Given a function F that generates random numbers between 1 and 5, how would I write a function G, using F, to generate random numbers between 1 and 7?
The probability of each number generated by G should be the same.
So far, I have tried the equation G=F + (5*(F/7)), although I'm not sure that all of the probabilities are the same.
Pseudocode:
function g()
repeat
// result will be equidistributed in 0..24
result = (f()-1)*5 + (f()-1);
// wait till we have something below 21, on average this happens 21 out of 25 times in the first pass
if (result < 21): return 1 + (result % 7)

Better random "feeling" integer generator for short sequences

I'm trying to figure out a way to create random numbers that "feel" random over short sequences. This is for a quiz game, where there are four possible choices, and the software needs to pick one of the four spots in which to put the correct answer before filling in the other three with distractors.
Obviously, arc4random % 4 will create more than sufficiently random results over a long sequence, but in a short sequence its entirely possible (and a frequent occurrence!) to have five or six of the same number come back in a row. This is what I'm aiming to avoid.
I also don't want to simply say "never pick the same square twice," because that results in only three possible answers for every question but the first. Currently I'm doing something like this:
bool acceptable = NO;
do {
currentAnswer = arc4random() % 4;
if (currentAnswer == lastAnswer) {
if (arc4random() % 4 == 0) {
acceptable = YES;
}
} else {
acceptable = YES;
}
} while (!acceptable);
Is there a better solution to this that I'm overlooking?
If your question was how to compute currentAnswer using your example's probabilities non-iteratively, Guffa has your answer.
If the question is how to avoid random-clustering without violating equiprobability and you know the upper bound of the length of the list, then consider the following algorithm which is kind of like un-sorting:
from random import randrange
# randrange(a, b) yields a <= N < b
def decluster():
for i in range(seq_len):
j = (i + 1) % seq_len
if seq[i] == seq[j]:
i_swap = randrange(i, seq_len) # is best lower bound 0, i, j?
if seq[j] != seq[i_swap]:
print 'swap', j, i_swap, (seq[j], seq[i_swap])
seq[j], seq[i_swap] = seq[i_swap], seq[j]
seq_len = 20
seq = [randrange(1, 5) for _ in range(seq_len)]; print seq
decluster(); print seq
decluster(); print seq
where any relation to actual working Python code is purely coincidental. I'm pretty sure the prior-probabilities are maintained, and it does seem break clusters (and occasionally adds some). But I'm pretty sleepy so this is for amusement purposes only.
You populate an array of outcomes, then shuffle it, then assign them in that order.
So for just 8 questions:
answer_slots = [0,0,1,1,2,2,3,3]
shuffle(answer_slots)
print answer_slots
[1,3,2,1,0,2,3,0]
To reduce the probability for a repeated number by 25%, you can pick a random number between 0 and 3.75, and then rotate it so that the 0.75 ends up at the previous answer.
To avoid using floating point values, you can multiply the factors by four:
Pseudo code (where / is an integer division):
currentAnswer = ((random(0..14) + lastAnswer * 4) % 16) / 4
Set up a weighted array. Lets say the last value was a 2. Make an array like this:
array = [0,0,0,0,1,1,1,1,2,3,3,3,3];
Then pick a number in the array.
newValue = array[arc4random() % 13];
Now switch to using math instead of an array.
newValue = ( ( ( arc4random() % 13 ) / 4 ) + 1 + oldValue ) % 4;
For P possibilities and a weight 0<W<=1 use:
newValue = ( ( ( arc4random() % (P/W-P(1-W)) ) * W ) + 1 + oldValue ) % P;
For P=4 and W=1/4, (P/W-P(1-W)) = 13. This says the last value will be 1/4 as likely as other values.
If you completely eliminate the most recent answer it will be just as noticeable as the most recent answer showing up too often. I do not know what weight will feel right to you, but 1/4 is a good starting point.

Resources