Five unique, random numbers from a subset - php

I know similar questions come up a lot and there's probably no definitive answer, but I want to generate five unique random numbers from a subset of numbers that is potentially infinite (maybe 0-20, or 0-1,000,000).
The only catch is that I don't want to have to run while loops or fill an array.
My current method is to simply generate five random numbers from a subset minus the last five numbers. If any of the numbers match each other, then they go to their respective place at the end of the subset. So if the fourth number matches any other number, it will bet set to the 4th from the last number.
Does anyone have a method that is "random enough" and doesn't involve costly loops or arrays?
Please keep in mind this a curiosity, not some mission-critical problem. I would appreciate it if everyone didn't post "why are you having this problem?" answers. I am just looking for ideas.
Thanks a lot!

One random number call is enough.
If you want to choose a subset of 5 unique numbers in range 1-n, then select a random number in 1 to (n choose r).
Keep a 1-1 mapping from 1 to (n choose r) to the set of possible 5 element subsets, and you are done. This mapping is standard and can be found on the web, for instance here: http://msdn.microsoft.com/en-us/library/aa289166%28VS.71%29.aspx
As an example:
Consider the problem of generating a subset of two numbers from five numbers:
The possible 2 element subset of {1,..., 5} are
1. {1,2}
2. {1,3}
3. {1,4}
4. {1,5}
5. {2,3}
6. {2,4}
7. {2,5}
8. {3,4}
9. {3,5}
10. {4,5}
Now 5 choose 2 is 10.
So we select a random number from 1 to 10. Say we got 8. Now we generate the 8th element in the sequence above: which gives {3,4}, so the two numbers you want are 3 and 4.
The msdn page I linked to, shows you a method to generate the set, given the number. i.e. given 8, it gives back the set {3,4}.

Your best option is a loop, as in:
$max = 20;
$numels = 5;
$vals = array();
while (count($vals) < $numels) {
$cur = rand(0, $max);
if (!in_array($cur, $vals))
$vals[] = $cur;
}
For small ranges, you can use array_rand:
$max = 20;
$numels = 5;
$range = range(0, $max);
$vals = array_rand($range, $numels);
You could also generate a number between 0 and max, another between 0 and max-1, ... between 0 and max-4. Then you would sum x to the n-th generated number where x is the number calculated in this fashion:
Take the number generated in the n-th iteration and assign it to x
if it's larger or equal to that generated in the first iteration, increment it
if this new number is larger or equal to that generated (and corrected) in the second iteration, increment it
...
if this new number is larger or equal to that generated (and corrected) in the (n-1)-th iteration increment it
The mapping is like this:
1 2 3 4 5 6 7 8 9 (take 4)
1 2 3 4 5 6 7 8 9 (gives 4)
1 2 3 4 5 6 7 8 (take 5)
1 2 3 5 6 7 8 9 (gives 6)
1 2 3 4 5 6 7 (take 6)
1 2 3 5 7 8 9 (gives 8)
1 2 3 4 5 6 (take 5)
1 2 3 5 7 9 (gives 7)
example, last extraction:
x = 5
x >= 4? x == 6
x >= 6? x == 7
x >= 8? x == 7

The general form of this question is really interesting. Should one select from a pool of elements (and remove them from the pool) or should one loop "while hitting" an already taken element?
As far as I can tell, the python library implementation for random.sample chooses at runtime between the two methods depending on the proportion of the size of the input list and the number of elements to select.
A comment from the source code:
# When the number of selections is small compared to the
# population, then tracking selections is efficient, requiring
# only a small set and an occasional reselection. For
# a larger number of selections, the pool tracking method is
# preferred since the list takes less space than the
# set and it doesn't suffer from frequent reselections.
In the specific instance that the OP mentions however (selecting 5 numbers), I think that looping "while hitting a taken number" is ok, unless the pseudo random generator is broken.

Since you are just looking for different ideas here's one:
Call out to Random.org to generate the set of random numbers you need.

If you know the size N then keep each number with probability 5/N generate a random number between 0 and 1 and if it is less than 5/N keep the item. Stop when we have 5 items.
If we don't know N use resorvoir sampling.

An implementation of Artefacto's second solution above in C#, as a helper and an extension method on ICollection:
static class Program {
public static IEnumerable<int> Subset(int max) {
Random random = new Random();
List<int> selections = new List<int>();
for (int space = max; space > 0; space--) {
int selection = random.Next(space);
int offset = selections.TakeWhile((n, i) => n <= selection + i).Count();
selections.Insert(offset, selection + offset);
yield return selection + offset;
}
}
public static IEnumerable<T> Random<T>(this ICollection<T> collection) {
return Subset(collection.Count).Select(collection.ElementAt);
}
static void Main(string[] args) {
Subset(10000).Take(10).ToList().ForEach(Console.WriteLine);
"abcdefghijklmnopqrstuvwxyz".ToArray().Random().Take(5).ToList().ForEach(Console.WriteLine);
}
}

I know we are trying to avoid loops, but just in case this helps someone, you can use a HashSet instead of a List. This is very efficient on a sparse collection where collisions are somewhat rare.
var hs = new HashSet<int>();
var rand = new Random();
for(int i=0; i<10000; i++)
{
int n;
while(true)
{
n = rand.Next(0, 10000000);
if(!hs.Contains(n)) {break;}
}
hs.Add(n);
}

Related

How to Generate random number within specific number

I need to generate three different random numbers without repeating, Three different random numbers need to be within 10 of the answer
for the sample IQ Question: 4,6 ,9,6,14,6,... Ans:19
A: random numbers
B: random numbers
C: random numbers
D: random numbers
one of them is the answer
I am now using the following code but sometimes the numbers are repeated, I have tried shuffle But which one is really random cannot satisfy random numbers need to be within 10 of the answer
$ans = $row['answer'];
$a = rand (1,10);
$a1 = rand($ans-$a ,$ans+$a);
$a2 = rand($ans-$a ,$ans+$a);
$a3 = rand($ans-$a ,$ans+$a);
As shown in previous answers (e.g. Generating random numbers without repeats, Simple random variable php without repeat, Generating random numbers without repeats) you can use shuffle to randomise a range, and then pick three items using array_slice.
The difference in your case is how you define the range:
Rather than 1 to 10, you want $ans - 10 to $ans + 10
You want to exclude the right answer
One way to build that is as two ranges: lower limit up to but not including right answer, and right answer + 1 up to upper limit.
function generate_wrong_answers($rightAnswer) {
// Generate all wrong guesses from 10 below to 10 above,
// but miss out the correct answer
$wrongAnswers = array_merge(
range($rightAnswer - 10, $rightAnswer - 1),
range($rightAnswer + 1, $rightAnswer + 10)
);
// Randomise
shuffle($wrongAnswers);
// Pick 3
return array_slice($wrongAnswers, 0, 3);
}

Store many numbers as a single unique number

I have the necessity to store many numbers (i can decide which numbers) as a single unique number from which i should be able to retrieve the original number.
I already know 2 ways to do this:
1) Fundamental theorem of arithmetic (Prime Numbers)
Say i have 5 values, i assign a prime number other than 1 to each value
a = 2
b = 3
c = 5
d = 7
e = 13
If i want to store a, b and c i can multiply them 2*3*5=30 and i know no other product of primes can be 30. Then to check if a value contains, for example, b, all i need to do is 30 % b == 0
2) Bitmask
Just like Linux permissions, use powers of 2 and sum each value
But these 2 methods grow up fast (1st way faster than 2nd), and using prime numbers requires me to have a lot of primes.
Is there any other method to do this efficiently when you have, for example, a thousand values?
If you are storing, say, base 10 numbers, then do a conversion through base 11 numbers. With the increased base, you have an extra 'digit'. Use that digit as a separator. So, three base 10 numbers "10, 42, 457" become "10A42A457": a single base 11 number (with 'A' as the additional digit).
Whatever base your original numbers are in, increase the base by 1 and concatenate, using the extra digit as a separator. That will give you a single number in the increased base.
That single number can be stored in whatever number base you find convenient: binary, denary or hex for example.
To retrieve your original numbers just convert to base 11 (or whatever) and replace the extra digit with separators.
ETA: You don't have to use base 11. The single number "10A42A457" is also a valid hexadecimal number, so any base of 11 or above could be used. Hex may be easier to work with than base 11.
Is there any other method to do this efficiently when you have, for example, a thousand values?
I an not a mathematician but it's basic math, all depends on range
Range 0-1: You want to store 4 numbers 0-1 - it's basically binary system
Number1 + Number2 * 2^1 + Number3 * 2^2 + Number4 * 2^3
Range 0-50 You want to store 4 numbers 0-49
Number1 + Number2 * 50^1 + Number3 * 50^2 + Number4 * 50^3
Range 0-X You want to store N numbers 0-X
Number1 + Number2 * (X+1)^1 + Number3 * (X+1)^2 + ... + NumberN * (X+1)^(N-1)
If you have no pattern for your numbers (so it can get compressed in some way) there is really no other way.
It's also super easy for computer to resolve the number unlike the prime numbers
Predetermined values
#FlorainK comment pointed me to fact I missed
(i can decide which numbers)
The only logical solution is give your numbers references
0 is 15342
1 is 6547
2 is 76234
3 is "i like stack overflow"
4 is 42141
so you'll work range 0-4 (5 options) and whatever combination length. Use reference when "encoding" and "decoding" the number
a thousand values?
so you'll work with Range 0-999
0 is 62342
1 is 7456345653
2 is 45656234532
...
998 is 7623452
999 is 4324234326453
Let's say you use 64-bit system and programming/db language that works with 64-bit integers
2^64 = 18446744073709551616
your max range is 1000^X < 18446744073709551616 where X is number of numbers you can store in one single 64-bit integer number
Which is only 6.
You can store only 6 separate numbers 0-999 that will fit one 64-bit integer number.
0,0,0,0,0,0 is 0
1,0,0,0,0,0 is 1
0,1,0,0,0,0 is 1000
999,999,999,999,999,999 is ~1e+18
Ok so you want to store "a,b,c" or "a,b" or "a,b,c,d" or "a" etc. (thanks #FlorianK)
in such case just could use bitwise operators and powers of two
$a = 1 << 0; // 1
$b = 1 << 1; // 2
$c = 1 << 2; // 4
$d = 1 << 3; // 8
.. etc
let's say $flag has $a and $c
$flag = $a | $c; // $flag is integer here
now check it
$ok = ($flag & $a) && ($flag & $c); // true
$ok = ($flag & $a) && ($flag & $b); // false
so in 64 bit system/language/os you can use up to 64 flags which gives you a 2^64 combinations
there is no really other option. prime numbers are much worse for this as you skip many numbers in-between while binary system uses every single number.
I see you are using database and you want to store this in DB.
I really think we are dealing here with XY Problem and you should reconsider your application instead of making such workarounds.

Solving 3x3 puzzle with PHP using Breadth-first search

i am making a 3x3 puzzle solver using php. Zero is the free space, where you can move. For example:
1 2 3
4 0 5
7 8 6
To
1 2 3
4 5 0
7 8 6
To
1 2 3
4 5 6
7 8 0
I already made random generator - 50 random moves are made. But I'm stack, with the solver algorithm.
The output should be all the steps to solve it.
I already got working method to solve one-step puzzle, but i dont know how to use it recursively.
public function makeMoves($elements)
{
$pos = $this->findSpace($elements); //returns position of the free space
$actions = $this->findActions($pos); //returns all actions positions (left, right, top, bottom)
$possibleActions = $this->findPossibleActions($actions); //return number of possible actions
for ($i = 1; $i <= $possibleActions; ++$i) { //let's do all possible actions
$move = $this->selectAction($actions, $i, $pos); //get new position for the space
$perform = $this->performAction($elements, $pos, $move); //swap the space with the element on that position
$this->tree[] = new Elements;
end($this->tree);
$last_id = key($this->tree);
$this->tree[$last_id]->setState($perform);
$this->tree[$last_id]->setAncestor(0);
$step = [$move, $pos];
$this->tree[$last_id]->setStep($step);
if ($perform == $this->elementsDone) {
return $this->tree[$last_id];
}
}
}
One solution is to use the A* algorithm to find the shortest path to a solution. Each move has cost 2. Each position has a distance from the desired solution of the sum of the distances each piece has to move. (One corner to the other is distance 4.) You are guaranteed to find the shortest solution if there is one.
See http://www.briangrinstead.com/blog/astar-search-algorithm-in-javascript for an implementation of that algorithm.
Be warned that half of all random configurations will NOT be solvable. See https://www.cs.bham.ac.uk/~mdr/teaching/modules04/java2/TilesSolvability.html for a test to tell you which ones to throw away. It also gives hints for how to write an algorithm which takes less memory than the one I suggested and finds inefficient solutions, but will do less work to find those solutions.

10 digit mt_rand() with unbiased first digit

I want to generate the profile ids in my software. The mt_rand function works well but I need the ids to be a fixed 10 digit long. Currently I am looping through mt_rand outputs until I get a 10 digit number. But the problem I am facing now is that most of the profile ids start from 1 and some from 2. None from any of the other single digit numbers. I understand this happens because of mt_rand's range and it can't produce 10 digit numbers that start with 3 or more.
This is what I am currently doing
for($i = 0; $i < 200; $i++){
$num = mt_rand();
if(strlen($num) == 10) echo $num."<br>";
}
If you run the above code you will see all numbers start from either 1 or 2. Any way to fix this?
Edit: I guess I can just flip the numbers but some numbers end with zero and this seems like a bit of a hack anyways. But then again, random number generation is a hack in itself I guess.
just start your IDs at 1000000001 , then ID 2 at 1000000002 , ID 543 at 1000000543 , and so on?
alternatively, keep calling mt_rand(1000000001,min((PHP_INT_SIZE>4 ? intval("9999999999",10): PHP_INT_MAX),mt_getrandmax())) until you get an ID which does not already exist in your database? (this will be more and more cpu intesive as your db grows larger and larger.. when its almost full, i wouldn't be surprised if it took billions of iterations and several minutes..)
To elaborate on Rizier's suggestion, the only way to ensure any string (even a string of numbers) fits a given mold for length and rules is to generate it one character at a time and then fit them together
$str = '';
for($loop = 0; $loop < 10; $loop++) {
$str .= mt_rand(0,9);
}
echo $str;
You can then add rules to this. Maybe you don't want a leading 0 so you can add a rule for that. Maybe you want letters too. This will always give you a random string with the rules you want.
You can see this in action here http://3v4l.org/kIRdV

bcmod find another variable

I'm stacked at math question with php!
Let's say we have:
$first = "3707682248186045564102137590742467172304310498516787723642221858460240158712832";
$second = "23846232839228381";
$result = bcmod($first,$second);
echo $result;
// $result = 3433268;
How to calculate the $first variable (if we dont know it) if only we have $result and the $second value ?
Actually not exact value but should be close to it, so when doing
bcmod($first, $second);
it will give the $result value equal to 3433268
Yes,
bcmod('3433268',$second);
also gives 3433268!
I appreciate all your help, thank you guys!
Any number of numbers could have given that result. Recovering a number from its remainder when divided by something is impossible.
the problem is this: ... the mod function is not bijective ... in other words, there is no inverse function ...
let's say we calculate 10 mod 7 we get 3 ... (10 is congruent to 3 mod 7)
if we calculate 17 mod 7 we also get 3 ... (17 is also congruent to 3 mod 7)
24 mod 7 ... 3
so ... if i give you the 3 and the 7 ... and tell you the operation was "mod" ... which number do i want to hear from you? ... 10? ... 17? ... 24?
all those numbers belong the the same remainder class ... but over the body of real numbers, every remainder class has an infinite amount of members ... therefore, if you want to reverse "mod 7", there is an infinite amount of possible functions which are candidates for the inverse of the operation you want to reverse ... but you have no information which one will be the right one ...
by reducing a number to it's remainder class (the mod function does just this), information is lost ... there is no way of getting that information back ...

Categories