choosing items based on random numbers - c#

I am working on a small game where prizes are awarded based on a random number. So when a mission is completed and the reward has to be handed out, I want to randomly generate the prize. Currently I am getting a random number between 1 and 500 and then using a giant nested if-else statement to assign the prize based on the result. It is obvious to me that this is the wrong way to do this, however I am not sure what other way this may be done.
Any suggestions?

You can just use a prize array to hold your prices, use the random value as index into the array to pick a prize.
var prizes = new Prize[500];
//fill prizes
//randomly select index within prize array
If there are gaps or only a few numbers win anything, use a dictionary mapping from an integer to a prize ( or nothing if they key doesn't exist) instead.

There are many things you could do. For example, you could use an array to store the prizes and use the random number as the index of the array. The prize would then be whatever was stored in the array at that index. You could do the same with a List if you wanted more control.

Use a list where each item has a unique "minimum value" and information about the associated prize. Then find the item on the list that has the highest value, but is still below your random number. Or vice-versa.
The minimum value allows you to give different probabilities to prizes without using a giant array that has an entry for all numbers. The probabilities are defined by the distances of the values between different prizes.

In this example I assume that your prizes are strings (but this works with objects too).
Create a list and add all of your prizes to it, in the following way:
//using System.Collections.Generic;
var prices = new List<string>();
//Add all of your prices here
var random = new Random();
var price = prices[random.Next(0,prices.Count)];

Related

pseudo random number generation

I am generating a random number in my game using:
System.Random rnd = new System.Random ();
int TileType = rnd.Next (0, 8);
The problem is that the numbers are too random. The numbers relate to tiles and i didn't get a particular tile for 20 rolls.
I need guidance on making a function that is generates "fake" random numbers. So basically I would like to make sure i generate each tile type in x rolls. I imagine 12 might be a good starting point for x but would like to be able to adjust x.
If anyone could point me in the right direction it would be greatly appreciated.
Update
Heres what i am currently thinking. Thanks #Golo for pointing me this way. Thoughts?
Create a list of the numbers i want.
Add x more random numbers.
Shuffle list -var shuffledList = unshuffledList.OrderBy(x => rand.Next()).ToList();
Grab the first item and remove from list.
Recreate list when empty.
This should mean that all numbers will come up frequently but you can still have repeats so it will maintain a random feel.
Create a list with the numbers you want to have, and then randomly select an element from this list and remove it.
Repeat this until your list is empty, and start over again.
This way you will get each number once in n tries, but their order will be random.
I like #Golo's answer, but just to add a different spin on it . . .
It sounds like his response will only give you each value once. (ie: You will not see value n again until all other values are "rolled" once.)
To keep things truly random while slightly more predictable, try a weighted random number generator.
After each selection, you can 0 out the weight of the selected item (decreasing the chance of it being selected next, and increasing the chance of selecting the "oldest" unselected values.)
These aren't in .Net, but should get you started on the algorithm.
http://www.perlmonks.org/?node_id=242744
http://peterkellyonline.blogspot.com/2012/02/weighted-random-selection-in-php.html

Sorting without comparing elements

given size of array 5, with five numbers in it, sort them from smallest to largest without comparing.(hint, access time O(n)
I tried to search a lot but didnt knew, how it can be done. O(n), means which algo/data structure.i am unaware.
I suppose you need Counting sort, it has linear time, but takes some memory and depends on min/max value of your initial array
The Counting Sort will do this for you, although if I were in an interview and on the spot I'd probably do something like the below which is vaguely similar as I can never remember these "classic" algorithms off the top of my head!
The key idea here is to use the each actual unsorted integer value as an index into a target array that contains N elements where N is the max. of the values to be sorted.
I am using a simple class to record both the value and the number of times it occurred so you can reconstruct an actual array from it if you need to keep discrete values that occurred multiple times in the original array.
So all you need to do is walk the unsorted array once, putting each value into the corresponding index in the target array, and (ignoring empty elements) your values are already sorted from smallest to largest without having ever compared them to one another.
(I personally am not a fan of interview questions like this where the answer is "oh, use Counting Sort" or whatever - I would hope that the interviewer asking this question would be genuinely interested to see what approach you took to solving a new problem, regardless of if you got a strictly correct answer or not)
The performance of the below is O(n) meaning it runs in linear time (1 element takes X amount of time, 10 elements takes 10X,etc) but it can use a lot of memory if the max element is large,cannot do in place sorting, will only work with primitives and it's not something I'd hope to ever see in production code :)
void Main()
{
//create unsorted list of random numbers
var unsorted = new List<int>();
Random rand = new Random();
for(int x=0;x<10;x++)
{
unsorted.Add(rand.Next(1,10));
}
//create array big enough to hold unsorted.Max() elements
//note this is indirectly performing a comparison of the elements of the array
//but not for the sorting, so I guess that is allowable :)
var sorted = new NumberCount[unsorted.Max()+1];
//loop the unsorted array
for (int index=0;index<unsorted.Count;index++)
{
//get the value at the current index and use as an index to the target array
var value = unsorted[index];
//if the sorted array contains the value at the current index, just increment the count
if (sorted[value]!=null && sorted[value].Value!=0)
{
sorted[value].Count++;
}
else
{
//insert the current value in it's index position
sorted[value]=new NumberCount{Value=value,Count=1};
}
}
//ignore all elements in sorted that are null because they were not part of the original list of numbers.
foreach (var r in sorted.Where(r=>r!=null))
{
Console.WriteLine("{0}, occurs {1} times",r.Value,r.Count);
}
}
//just a poco to hold the numbers and the number of times they occurred.
public class NumberCount
{
public int Value{get;set;}
public int Count{get;set;}
}

How to generate 6-8 digit random numbers without colliding with previous generated numbers in time critical fashion?

In this scenarion I have some managers(around 150 in numbers). One of their daily job is to generate 50(constant) authorisation code (6-8 digit numbers) which are stored in db with their Id. If any authorisation code is used that code is marked as used and triggers delete them when they are 15 days old and have been used.
In my table i have set authorisation code as unique key. i generate a random number then query the db if it exists i generate another or i else save it.
Every thing is fine except my logic of checking the existence of number in db.This round trip + checking is causing significant delay as of now there are over 1090083 pending authorisation code. Since these authorisation code are in circulation we cant revoke it and with current load it is taking sometime to find new numbers.
I need to implement it in a different logic for which execution speed should be low regardles of number of random number that has been used.
My table is designed as follows
slno(auth increment) || auth_code (random code) || auth_by (created by) || used
(1=used/0=unused)
The easiest thing to do is generate random numbers and generating a new random id if you get a duplicate. This works because with your figures the probability of getting a duplicate is pretty small.
If that doesnt convince you, you can think of many schemes that guarantee mathematically that the numbers will be unique and still look random, but it gets complex.
Consider this. If randoms are unique and stored in a base in some kind of (code_id, code, other_data) table way, you can just add anoter table in your base: (code, code_id) with the code field being indexed granting you some nice logariphmycal search.
But given this, you can also create an additional key right in your first table instead. As soon as code is unique, it would work fine.
If your database does not support to create unique ids:
- Set up a table with all random numbers which are sorted by value and its
size is stored and available.
Randomly select an element of this table.
Get the successor element. If the successor element is a neighbor of the element,
take the next successor element. If you reach the last element, start over with the
element from step 2 and take now the predecessor.
Now simply choose a random range with element-next element and get your random number.
Ready !
EXAMPLE: You stored all your ids in a sorted table. Lets assume this is e.g.
{890, 1045, 2345, 2346, 4087}
First step: Select one of them randomly. You get that e.g. by C#
Random random = new Random();
int indexOfNumber = random.Next(0, myTableSize);
Second step: You got the index, lets assume it is 2. You are now getting the next number at index 3, it is 2346. Unfortunately it is a direct neighbor, so you continue to index 4.
This is 4087.
Third step: Create your number by
int myRandomNumber = previousElement + random.Next(1,nextElement-previousElement);
in this case:
int myRandomNumber = 2346 + random.Next(1, 4087-2346);
Store the new random number.
With this you will read mostly two elements from the database (probably some more) independent of the size of the database. Creating two random numbers is insignificant.
You must only care for the edge cases if your index is at the end (simply reverse the search direction).

smart way to generate unique random number

i want to generate a sequence of unique random numbers in the range of 00000001 to 99999999.
So the first one might be 00001010, the second 40002928 etc.
The easy way is to generate a random number and store it in the database, and every next time do it again and check in the database if the number already exists and if so, generate a new one, check it again, etc.
But that doesn't look right, i could be regenerating a number maybe 100 times if the number of generated items gets large.
Is there a smarter way?
EDIT
as allways i forgot to say WHY i wanted this, and it will probably make things clearer and maybe get an alternative, and it is:
we want to generate an ordernumber for a booking, so we could just use 000001, 000002 etc. But we don't want to give the competitors a clue of how much orders are created (because it's not a high volume market, and we don't want them to know if we are on order 30 after 2 months or at order 100. So we want to have an order number which is random (yet unique)
You can use either an Linear Congruential Generator (LCG) or Linear Feedback Shift Register (LFSR). Google or wikipedia for more info.
Both can, with the right parameters, operate on a 'full-cycle' (or 'full period') basis so that they will generate a 'psuedo-random number' only once in a single period, and generate all numbers within the range. Both are 'weak' generators, so no good for cyptography, but perhaps 'good enough' for apparent randomness. You may have to constrain the period to work within your 'decimal' maximum as having 'binary' periods is necessary.
Update: I should add that it is not necessary to pre-calculate or pre-store previous values in any way, you only need to keep the previous seed-value (single int) and calculate 'on-demand' the next number in the sequence. Of course you can save a chain of pre-calculated numbers to your DB if desired, but it isn't necessary.
How about creating a set all of possible numbers and simply randomising the order? You could then just pick the next number from the tail.
Each number appears only once in the set, and when you want a new one it has already been generated, so the overhead is tiny at the point at which you want one. You could do this in memory or the database of your choice. You'll just need a sensible locking strategy for pulling the next available number.
You could build a table with all the possible numbers in it, give the record a 'used' field.
Select all records that have not been 'used'
Pick a random number (r) between 1 and record count
Take record number r
Get your 'random value' from the record
Set the 'used' flag and update the db.
That should be more efficient than picking random numbers, querying the database and repeat until not found as that's just begging for an eternity for the last few values.
Use Pseudo-random Number Generators.
For example - Linear Congruential Random Number Generator
(if increment and n are coprime, then code will generate all numbers from 0 to n-1):
int seed = 1, increment = 3;
int n = 10;
int x = seed;
for(int i = 0; i < n; i++)
{
x = (x + increment) % n;
Console.WriteLine(x);
}
Output:
4
7
0
3
6
9
2
5
8
1
Basic Random Number Generators
Mersenne Twister
Using this algorithm might be suitable, though it's memory consuming:
http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Put the numbers in the array from 1 to 99999999 and do the shuffle.
For the extremely limited size of your numbers no you cannot expect uniqueness for any type of random generation.
You are generating a 32bit integer, whereas to reach uniqueness you need a much larger number in terms around 128bit which is the size GUIDs use which are guaranteed to always be globally unique.
In case you happen to have access to a library and you want to dig into and understand the issue well, take a look at
The Art of Computer Programming, Volume 2: Seminumerical Algorithms
by Donald E. Knuth. Chapter 3 is all about random numbers.
You could just place your numbers in a set. If the size of the set after generation of your N numbers is too small, generate some more.
Do some trial runs. How many numbers do you have to generate on average? Try to find out an optimal solution to the tradeoff "generate too many numbers" / "check too often for duplicates". This optimal is a number M, so that after generating M numbers, your set will likely hold N unique numbers.
Oh, and M can also be calculated: If you need an extra number (your set contains N-1), then the chance of a random number already being in the set is (N-1)/R, with R being the range. I'm going crosseyed here, so you'll have to figure this out yourself (but this kinda stuff is what makes programming fun, no?).
You could put a unique constraint on the column that contains the random number, then handle any constraint voilations by regenerating the number. I think this normally indexes the column as well so this would be faster.
You've tagged the question with C#, so I'm guessing you're using C# to generate the random number. Maybe think about getting the database to generate the random number in a stored proc, and return it.
You could try giving writing usernames by using a starting number and an incremental number. You start at a number (say, 12000), then, for each account created, the number goes up by the incremental value.
id = startValue + (totalNumberOfAccounts * inctrementalNumber)
If incrementalNumber is a prime value, you should be able to loop around the max account value and not hit another value. This creates the illusion of a random id, but should also have very little conflicts. In the case of a conflicts, you could add a number to increase when there's a conflict, so the above code becomes. We want to handle this case, since, if we encounter one account value that is identical, when we increment, we will bump into another conflict when we increment again.
id = startValue + (totalNumberOfAccounts * inctrementalNumber) + totalConflicts
By fallowing line we can get e.g. 6 non repetitive random numbers for range e.g. 1 to 100.
var randomNumbers = Enumerable.Range(1, 100)
.OrderBy(n => Guid.NewGuid())
.Take(6)
.OrderBy(n => n);
I've had to do something like this before (create a "random looking" number for part of a URL). What I did was create a list of keys randomly generated. Each time it needed a new number it simply randomly selected a number from keys.Count and XOR the key and the given sequence number, then outputted XORed value (in base 62) prefixed with the keys index (in base 62).
I also check the output to ensure it does not contain any naught words. If it does simply take the next key and have a second go.
Decrypting the number is equally simple (the first digit is the index to the key to use, a simple XOR and you are done).
I like andora's answer if you are generating new numbers and might have used it had I known. However if I was to do this again I would have simply used UUIDs. Most (if not every) platform has a method for generating them and the length is just not an issue for URLs.
You could try shuffling the set of possible values then using them sequentially.
I like Lazarus's solution, but if you want to avoid effectively pre-allocating the space for every possible number, just store the used numbers in the table, but build an "unused numbers" list in memory by adding all possible numbers to a collection then deleting every one that's present in the database. Then select one of the remaining numbers and use that, adding it to the list in the database, obviously.
But, like I say, I like Lazaru's solution - I think that's your best bet for most scenarios.
function getShuffledNumbers(count) {
var shuffledNumbers = new Array();
var choices = new Array();
for (var i = 0; i<count; i++) {
// choose a number between 1 and amount of numbers remaining
choices[i] = selectedNumber = Math.ceil(Math.random()*(99999999 - i));
// Now to figure out the number based on this selection, work backwards until
// you figure out which choice this number WOULD have been on the first step
for (var j = 0; j < i; j++) {
if (choices[i - 1 - j] >= selectedNumber) {
// This basically says "it was choice number (selectedNumber) on the last step,
// but if it's greater than or equal to this, it must have been choice number
// (selectedNumber + 1) on THIS step."
selectedNumber++;
}
}
shuffledNumbers[i] = selectedNumber;
}
return shuffledNumbers;
}
This is as fast a way I could think of and only uses memory as it needs, however if you run it all the way through it will use double as much memory because it has two arrays, choices and shuffledNumbers.
Running a linear congruential generator once to generate each number is apt to produce rather feeble results. Running it through a number of iterations which is relatively prime to your base (100,000,000 in this case) will improve it considerably. If before reporting each output from the generator, you run it through one or more additional permutation functions, the final output will still be a duplicate-free permutation of as many numbers as you want (up to 100,000,000) but if the proper functions are chosen the result can be cryptographically strong.
create and store ind db two shuffled versions(SHUFFLE_1 and SHUFFLE_2) of the interval [0..N), where N=10'000;
whenever a new order is created, you assign its id like this:
ORDER_FAKE_INDEX = N*SHUFFLE_1[ORDER_REAL_INDEX / N] + SHUFFLE_2[ORDER_REAL_INDEX % N]
I also came with same kind of problem but in C#. I finally solved it. Hope it works for you also.
Suppose I need random number between 0 and some MaxValue and having a Random type object say random.
int n=0;
while(n<MaxValue)
{
int i=0;
i=random.Next(n,MaxValue);
n++;
Write.Console(i.ToString());
}
the stupid way: build a table to record, store all the numble first, and them ,every time the numble used, and flag it as "used"
System.Random rnd = new System.Random();
IEnumerable<int> numbers = Enumerable.Range(0, 99999999).OrderBy(r => rnd.Next());
This gives a randomly shuffled collection of ints in your range. You can then iterate through the collection in order.
The nice part about this is that you're not actually creating the entire collection in memory.
See comments below - this will generate the entire collection in memory when you iterate to the first element.
You can genearate number like below if you are ok with consumption of memory.
import java.util.ArrayList;
import java.util.Collections;
public class UniqueRandomNumbers {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i=1; i<11; i++) {
list.add(i);
}
Collections.shuffle(list);
for (int i=0; i<11; i++) {
System.out.println(list.get(i));
}
}
}

C# algorithm - listing all permutations of number

This is sort of a follow up to a question I posted earlier (C# algorithm - find least number of objects necessary), but a bit different.
Given I have the following code:
var max = 80;
var list = new[]{10,20,30,40,50, 60);
I want to generate a array containing all the possible combinations I can use those numbers in the list to get to that max number.
The array would contain, {40, 40}, {50, 30}, {40,30, 10} etc...
You'll want to iterate over all the numbers in descending order. Then recursively add each next descending number in the sequence. Each time the sum matches, note that combo, pop out, and move on. When your tentative sum takes you over the max variable, pop out of the function to the next function in the stack. If the max still hasn't been reached, successively add the next number down in the sequence. In this way you will cover ever possible sequence, with no duplicates (unless there are duplicates in the given set, in which case you would want that duplicate). It will be not too much code actually.
The naive approach is to simply generate every combination of numbers possible, and see if they add up to the target number.
Needless to say, this has hideous time complexity. But it does the job for small lists.
EDIT: Actually, if you're allowing repeated numbers, this doesn't work. An alternative algorithm (which allows repeats, but not any negatives) is to basically keep adding up the highest number in the list, and then backtrack if you go over the target.

Categories