I have the following scenario:
Generate n numbers of random number from a certain range
Sum all the numbers
Check if the sum == x (x is a number set by user)
if sum != x then keep running the loop
If sum == x, then display the list of random numbers that sum up to x
Based on this logic, I was able to do so but it takes forever to achieve the result, is there any better / way to solve this problem?
static void Main(string[] args)
{
Console.WriteLine("starting...");
List<int> checkList = generate(5);
while(!checkSum(checkList, 100))
{
checkList = generate(5)
}
Console.WriteLine("done!");
}
private static bool checkSum(List<int> n, int sum)
{
if(n.Sum() == sum)
{
return true;
}
else
{
return false;
}
}
public static List<int> generate(int n)
{
Random rng = new Random();
List<int> list = new List<int>();
for (int i = 0; i < 5; i++)
{
//int ran = some random number
list.Add(ran);
}
return list;
}
EDIT
My scenario here is to obtain n combinations of random integer that sums up to 100. The number of combinations is taken from input by user. So the program will give the n number of possible combinations that sums up to 100.
Possible combinations:
25+37+9+20+9 = 100
46+21+13+8+12 = 100
If you have a fixed sum and a fixed number of elements you want, look at the problem as partitioning. For example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PartitionAndAllocateTEst
{
class Program
{
static void Main(string[] args)
{
var rand = new Random();
int desiredTotal = 100;
int partitions = 5;
for (int i = 0; i < 10; i++)
{
List<int> result = GetRandomCollection(rand, desiredTotal, partitions);
Console.WriteLine(string.Join(", ", result.Select(r => r.ToString()).ToArray()));
}
}
private static List<int> GetRandomCollection(Random rand, int desiredTotal, int partitions)
{
// calculate the weights
var weights = new List<double>();
for (int i = 0; i < partitions; i++)
{
weights.Add(rand.NextDouble());
}
var totalWeight = weights.Sum();
// allocate the integer total by weight
// http://softwareengineering.stackexchange.com/questions/340393/allocating-an-integer-sum-proportionally-to-a-set-of-reals/340394#340394
var result = new List<int>();
double allocatedWeight = 0;
int allocatedCount = 0;
foreach (var weight in weights)
{
var newAllocatedWeight = allocatedWeight + weight;
var newAllocatedCount = (int)(desiredTotal * (newAllocatedWeight / totalWeight));
var thisAllocatedCount = newAllocatedCount - allocatedCount;
allocatedCount = newAllocatedCount;
allocatedWeight = newAllocatedWeight;
result.Add(thisAllocatedCount);
}
return result;
}
}
}
Example output:
30, 6, 19, 15, 30
36, 8, 22, 10, 24
2, 25, 32, 21, 20
22, 7, 30, 12, 29
36, 21, 22, 0, 21
24, 24, 2, 29, 21
18, 13, 10, 39, 20
11, 19, 20, 27, 23
24, 19, 7, 25, 25
24, 14, 27, 18, 17
You could try using a genetic algorithm. Start with a population of, say, 100 sets of five random integers. Sum each set. Select 50 of the better sets, the ones with a sum closer to 100. Keep the better 50, dump the other 50 and replace them with tweaked versions of the best 50. The tweak is replacing one integer in the set with another random integer.
Whenever a member of the population sums to exactly 100, pull it out into your output array and make up the population with a newly generated set of five integers.
Genetic algorithms work a lot faster than brute force.
I don't see why you're being downvoted. Your problem is clear, and you already knew you had a bad algorithm.
I would solve this in two passes. Pass 1 is figuring out how many ways there are to get from each partial point you could be to a final answer. Pass 2 is picking a random path.
For pass 1 you build up an array of arrays. By number of numbers left to pick, by what you need them to sum to, how many ways are there to get that answer? (Search for dynamic programming for more on how to do this.)
In pass 2 you go forward. You know at each step how many ways there are to complete your task, and how many there are for each value you can pick. You therefore know the probability of picking each value. So pick a random value in (0, 1), walk through the answers, pick your next number, and proceed.
This general strategy is not just for numbers. It can be used to generate a random sample of anything that you can count using dynamic programming techniques.
Related
I'm trying to create a lottery number program that accepts 10 guesses from the user between the numbers 1-25. The user guesses are stored in the user array. If the user enters the wrong value (< 1 or > 25) the number will not be stored within the user array. The program stores 10 random numbers in another array. So, two arrays in total.
How to compare the two arrays to find common elements, to let the user know how many correct guesses the user made compared to the random numbers array? For this part, I am only allowed to use loops.
I have tried using continue; below the first If, but that messes things up.
I have tried to move the ending curly brackets for the first for-loop to below the first If, but that also messes things up at the "compare the two arrays" part.
//User array
int[] användarTal = new int[10];
//Random numbers array
int[] slumpadeTal = new int[10];
//Generator for 10 random numbers.
Random randomerare = new Random();
//Foor-loop for storing user's 10 guesses in the user array.
for (int i = 0; i < användarTal.Length; i++)
{
Console.Write("Enter your guess: ");
string input = Console.ReadLine();
int num = Convert.ToInt32(input);
användarTal[i] = num;
//If the user enters wrong value
if (num < 1 || num > 25)
{
Console.WriteLine("Wrong! You must enter a number between 1-25!");
användarTal[i] = i--; //Wrong number will not be stored in the user array.
}
// continue;
// }
//For-loop for storing and writing out the 10 random numbers.
for (int j = 0; j < slumpadeTal.Length; j++)
{
Console.WriteLine(randomerare.Next(1, 26));
// Checking if the two arrays have any common numbers
if (användarTal[i] == slumpadeTal[j])
{
Console.WriteLine(användarTal[i]);
}
}
I found this solution which I have tried to implement into my program, but I can't get the program to run the way it's supposed to.
int[] arr1 = { 12, 5, 45, 47, 22, 10 };
int[] arr2 = { 23, 45, 10, 58, 78, 77, 46 };
for (int i = 0; i < arr1.Length; i++)
{
for (int j = 0; j < arr2.Length; j++)
{
if (arr1[i] == arr2[j])
{
Console.WriteLine(arr1[i]);
}
}
}
int[] arr1 = { 12, 5, 45, 47, 22, 10 };
int[] arr2 = { 23, 45, 10, 58, 78, 77, 46 };
for (int i = 0; i < arr1.Length; i++)
{
for (int j = 0; j < arr2.Length; j++)
{
if (arr1[i] == arr2[j])
{
Console.WriteLine(arr1[i]);
}
}
}
I dont see an issue with this code, seems like it should execute fine.
However you can optimise this using Linq
int[] array1 = { 12, 5, 45, 47, 22, 10 };
int[] array2 = { 23, 45, 10, 58, 78, 77, 46 };
int[] commonElements = array1.Intersect(array2);
Console.WriteLine("Common Elements: {0}", string.Join(",", commonElements));
See https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.intersect?view=net-7.0
It seems that you want to store and guess unique numbers only.
To be sure that numbers are unique, let's use HashSet<int> instead
of arrays:
using System.Linq;
...
// Random numbers array
HashSet<int> slumpadeTal = Enumerable
.Range(1, 25)
.OrderBy(item => Random.Shared.NextDouble())
.Take(10)
.ToHashSet();
Then let's ask user to guess
HashSet<int> användarTal = new HashSet<int>();
while (användarTal.Count < slumpadeTal.Count) {
while (true) {
Console.Write("Enter your guess: ");
if (int.TryParse(Console.ReadLine(), out int guess) &&
guess >= 1 && guess <= 25) {
användarTal.Add(guess);
break;
}
Console.WriteLine("Incorrect guess; it should be 1..25");
}
}
Having both collections filled in you can easily compare them
int guessedRight = slumpadeTal
.Count(item => slumpadeTal.Contains(item));
I'm trying to rotate a List to the right by a certain specified numbers of places. I think that an array (or List) rotation can be seen as a circular, which means elements that fall off the end would wrap around to the beginning, and vice versa. A good example of this could possibly be if we have an array or list, then we rotated it to the right by three places, the result will be as follow:
Initial Array (or List): 20, 30, 40, 50, 60, 70
Rotated to the right by 3 places: 50, 60, 70, 20, 30, 40.
I've written some code down, based of what the little I understand of this concept. I would like to know, what's the correct way to do this manually (without any sort of fancy code nor LINQ) as it will help me to understand it better.
i'm asking for the manual way to solve this problem, not with anything fancy.
public void Test8(List<int> items, int places)
{
int[] copy = new int[items.Count];
items.CopyTo(copy, 0);
for (int i = 0; i < items.Count; i++)
{
int RotateRight = (i + places) % items.Count;
items[i] = copy[RotateRight];
}
}
Here is a approache with Linq with Skip() and Take().
List<int> iList = new List<int>() { 20, 30, 40, 50, 60, 70 };
int rotate = 3;
List<int> lResult = iList.Skip(rotate).Concat(iList.Take(rotate)).ToList();
another approach with a simple loop
int[] items = new int[] { 20, 30, 40, 50, 60, 70 };
int[] result = new int[items.Length];
int rotate = 3;
for (int i = 0; i < items.Length; i++)
{
result[i] = items[(i+rotate)% items.Length];
}
This question already has answers here:
How to get a random number from a range, excluding some values
(11 answers)
Closed 9 years ago.
Is it possible to pick a random number from a given range (1-90), but exclude certain numbers. The excluded numbers are dynamically created but lets say they are 3, 8, and 80.
I have managed to create random number generator but couldn't identify any functions that let me fulfill my requirements.
Random r = new Random();
this.num = r.Next(1, 90);
The numbers which are to be excluded are previously generated numbers. So, if the random number is one, this would then get added to the excluded numbers list.
Using some handy extension methods here, you can create a range of numbers and select randomly from that rage. For example, with these extension methods:
public static T RandomElement(this IEnumerable<T> enumerable)
{
return enumerable.RandomElementUsing(new Random());
}
public static T RandomElementUsing(this IEnumerable<T> enumerable, Random rand)
{
int index = rand.Next(0, enumerable.Count());
return enumerable.ElementAt(index);
}
You can apply these to a filtered range of numbers:
var random = Enumerable.Range(1, 90).Except(arrayOfRemovedNumbers).RandomElement();
Create a container which holds the numbers you do not want:
var excludedNumbers = new List<int> { 1, 15, 35, 89 };
Then use do something like:
Random random = new Random();
int number;
do
{
number = r.Next(1, 90);
} while (excludedNumbers.Contains(number));
// number is not in the excluded list now
Might not be the best choice but you can use a while loop to check the numbers you don't want
Random r = new Random();
this.num = r.Next(1, 90);
do
{
this.num = r.Next(1, 90);
}
while (this.num == 3 || this.num == 8 || this.num == 90);
For much numbers you can use an array or a list and loop through them
int[] exclude = { 3, 8, 90, 11, 24 };
Random r = new Random();
this.num = r.Next(1, 90);
do
{
this.num = r.Next(1, 90);
}
while (exclude.Contains(this.num));
Your latest update, which implies that each value can only be selected once, makes the problem easy.
Create a collection of values within the range.
Randomly shuffle the collection.
To"randomly" select an item, just return the first item in the collection, and remove it from the collection.
Random r = new Random();
this.num = r.Next(1, 90);
int excluded[] = new int[] { 3,8,80 }; // list any numbers in this array you want to exclude
for (int i = 0; i < excluded.Length; i++)
{
if (this.num == excluded[i])
{
this.num = r.Next(1, 90); // or you can try doing something else here
}
}
This solution does it in O(n) worst case where n is your list of exclusions, and constant memory. The code is a little longer but might be relevant if you:
Possibly have a huge list of exclusions
Need to run this many times
Have a large range
It preserves the random distribution in the sense that it actually skips the exclusion list and generates a random number within the range excluding the set.
This is the implementation:
private static int RandomInRangeExcludingNumbers(Random random, int min, int max, int[] excluded)
{
if (excluded.Length == 0) return random.Next(min, max);
//array should be sorted, remove this check if you
//can make sure, or sort the array before using it
//to improve performance. Also no duplicates allowed
//by this implementation
int previous = excluded[0];
for (int i = 1; i < excluded.Length; i++)
{
if (previous >= excluded[i])
{
throw new ArgumentException("excluded array should be sorted");
}
}
//basic error checking, check that (min - max) > excluded.Length
if (max - min <= excluded.Length)
throw new ArgumentException("the range should be larger than the list of exclusions");
int output = random.Next(min, max - excluded.Length);
int j = 0;
//set the start index to be the first element that can fall into the range
while (j < excluded.Length && excluded[j] < min) j++;
//skip each number occurring between min and the randomly generated number
while (j < excluded.Length && excluded[j] <= output)
{
j++;
output++;
while (excluded.Contains(output))
output++;
}
return output;
}
And a test function to make sure it works (over 100k elements)
private static void Test()
{
Random random = new Random();
int[] excluded = new[] { 3, 7, 80 };
int min = 1, max = 90;
for (int i = 0; i < 100000; i++)
{
int randomValue = RandomInRangeExcludingNumbers(random, min, max, excluded);
if (randomValue < min || randomValue >= max || excluded.Contains(randomValue))
{
Console.WriteLine("Error! {0}", randomValue);
}
}
Console.WriteLine("Done");
}
Make sure excludedNumbers is a HashSet for best performance.
var random = new Random();
var exludedNumbers = new HashSet<int>(new int[] { 3, 8, 80});
var randomNumber = (from n in Enumerable.Range(int.MinValue, int.MaxValue)
let number = random.Next(1, 90)
where !exludedNumbers.Contains(number)
select number).First();
Let's say there is a set of number
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
I want to find out several combinations in the set of number such that the summation of it equal to a known number, for example, 18. We can find out that 5, 6, 7 is matched (5+6+7=18).
Numbers in a combination cannot be repeated and the number in a set may not be consecutive.
I've wrote a C# program to do that. The program is random to pick up number to form a combination and check whether the summation of combination is equal to a known number. However, the combination the program found may be repeated and it makes the progress not effective.
I am wondering whether there is any efficient algorithm to find out such combination.
Here's part of my code.
int Sum = 0;
int c;
List<int> Pick = new List<int>();
List<int> Target = new List<int>() {some numbers}
Target.Sort();
while (!Target.Contains(Sum))
{
if (Sum > Target[Target.Count - 1])
{
Pick.Clear();
Sum = 0;
}
while (true)
{
if (Pick.IndexOf(c = Math0.rand(0, Set.Count - 1)) == -1)
{
Pick.Add(c);
}
//Summation Pick
Sum = 0;
for (int i = 0; i < Pick.Count; i++)
Sum += Set[Pick[i]];
if (Sum >= Target[Target.Count - 1])
break;
}
}
Result.Add(Pick);
You can use recursion. For any given number in the set, find the combinations of smaller numbers that adds up to the number:
public static IEnumerable<string> GetCombinations(int[] set, int sum, string values) {
for (int i = 0; i < set.Length; i++) {
int left = sum - set[i];
string vals = set[i] + "," + values;
if (left == 0) {
yield return vals;
} else {
int[] possible = set.Take(i).Where(n => n <= sum).ToArray();
if (possible.Length > 0) {
foreach (string s in GetCombinations(possible, left, vals)) {
yield return s;
}
}
}
}
}
Usage:
int[] set = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
foreach (string s in GetCombinations(set, 18, "")) {
Console.WriteLine(s);
}
Output:
1,2,4,5,6,
3,4,5,6,
1,2,3,5,7,
2,4,5,7,
2,3,6,7,
1,4,6,7,
5,6,7,
1,2,3,4,8,
2,3,5,8,
1,4,5,8,
1,3,6,8,
4,6,8,
1,2,7,8,
3,7,8,
2,3,4,9,
1,3,5,9,
4,5,9,
1,2,6,9,
3,6,9,
2,7,9,
1,8,9,
1,3,4,10,
1,2,5,10,
3,5,10,
2,6,10,
1,7,10,
8,10,
A possible alternative method. With a small set like this, you could use brute force. Your set {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} has 10 elements, and each element can be present or not present. That can be mapped to a binary number between 0 (= 0b0000000000) and 1023 (= 0b1111111111). Loop through the numbers from 0 to 1023, inclusive, and check the sum for the subset corresponding to the set bits of the binary representation of the number.
Maybe not the most useful for this particular question, but a good way to generate all possible subsets of a given set.
I am not quite sure how to go about this.
I need to generate 14,296 random numbers with different levels of probability.
so for example I need an array containing the numbers 18, 1, and 17. Each number has a different percent probability of occuring. So:
55% = 18
(7,862.8 times)
30% = 1
(4,288.8 times)
15% = 17
(2,144.4 times)
the result would be something like new Array () { 18, 18, 1, 17, 1, 18...}
If you'll always have the values as integer percentages, I'd fill a 100-element array with values according to the probability, so in this case your array would have 55 occurrences of 18, 30 occurrences of 1, and 15 occurrences of 17. Then you just need to pick 14,296 random values from that array. (i.e. pick an integer in the range [0, 100) and take that element.)
For different ways of expressing the probabilities, there are different approaches, of course. But if you're given integer percentages, this is an easily-understood option. (Another way is to scale all of the probabilities by the total, i.e. into a range of [0, 1), and then take a random double in that range.)
Divide the range of random generator into proportional segments, and, judging to which segment the next random number has fallen into, select the corresponding number from your set.
Something like (simplified):
const int numbers[3] = { 1, 17, 18 };
const int borders[2] = { 0.30*MAX_RANDOM, (0.30 + 0.15) * MAX_RANDOM };
int i = random.next(), num;
if (i < borders[0]) num = number[0];
else if (i < borders[0]) num = number[1];
else num = number[2];
Of course, if there's more numbers than three, it's better to use a loop.
Note: unlike Jon Skeet's solution, this one can provide any desired granularity up to 1/(MAX_RANDOM+1) (which is often up to 2^32 on 32-bit machines), rather than strictly 1%.
Random r = new Random();
// for each number to generate
int nextNumber;
double probability = r.NextDouble();
if (probability < 55.0 / 100.0)
nextNumber = 18;
else if (probability < (55.0 + 30.0) / 100.0)
nextNumber = 1;
else
nextNumber = 17;
How about something like this (untested):
struct np
{
int n;
int p;
}
Create a List<np> and will it with value/percentage pairs (e.g. n = 18, p = 55).
Then just do the following to pick a number:
List<np> npl = new List<np>();
// (fill the list here)
int r = rnd.next(total_of_all_p_values); // get random number
int res = 0; // result
for(int i = 0; i < npl.Length(); r -= npl[i++].n)
{
if(r < npl[i].p) // remaining vlaue is smaller than current percentage
{
res = npl[i].n;
break;
}
}
You could populate a List<T> with the appropriate number of each of the 3 numbers, and then randomize the List.