I have a problem I need to solve using C#. There is an array of decimal numbers (representing quantities of an item received by a warehouse at different times). This array is already sorted in the order in which the quantities were received. I need to be able to find the earliest combination of quantities that sum up to a specified total quantity.
So for example, say I have some quantities that came in chronologically as follows [13, 6, 9, 8, 23, 18, 4] and say my total quantity to match is 23. Then I should be able to get [13, 6, 4] as the matching subset although [6, 9, 8] and [23] are also matching but not the earliest.
What would be the best approach/algorithm for this?
I have so far come up with a rather naive approach using recursion.
public class MatchSubset
{
private decimal[] qty = null;
private decimal matchSum = 0;
public int operations = 0;
public int[] matchedIndices = null;
public int matchCount = 0;
private bool SumUp(int i, int n, decimal sum)
{
operations++;
matchedIndices[matchCount++] = i;
sum += qty[i];
if (sum == matchSum)
return true;
if (i >= n - 1)
{
matchCount--;
return false;
}
if (SumUp(i + 1, n, sum))
return true;
sum -= qty[i];
matchCount--;
return SumUp(i + 1, n, sum);
}
public bool Match(decimal[] qty, decimal matchSum)
{
this.qty = qty;
this.matchSum = matchSum;
matchCount = 0;
matchedIndices = new int[qty.Count()];
return SumUp(0, qty.Count(), 0);
}
}
static void Main(string[] args)
{
var match = new MatchSubset();
int maxQtys = 20;
Random rand = new Random(DateTime.Now.Millisecond);
decimal[] qty = new decimal[maxQtys];
for (int i = 0; i < maxQtys - 2; i++)
qty[i] = rand.Next(1, 500);
qty[maxQtys - 2] = 99910;
qty[maxQtys - 1] = 77910;
DateTime t1 = DateTime.Now;
if (match.Match(qty, 177820))
{
Console.WriteLine(DateTime.Now.Subtract(t1).TotalMilliseconds);
Console.WriteLine("Operations: " + match.operations);
for (int i = 0; i < match.matchCount; i++)
{
Console.WriteLine(match.matchedIndices[i]);
}
}
}
The matching subset can be as short as one element and as long as the original set (containing all elements). But to test the worst case scenario, in my test program I am using an arbitrarily long set of which only the last two match the given number.
I see that with 20 numbers in the set, it calls the recursive function over a million times with a max recursion depth of 20. If I run into a set of 30 or more numbers in production, I am fearing it will consume a very long time.
Is there a way to further optimize this? Also, looking at the downvotes, is this the wrong place for such questions?
I was unable to end up with something revolutionary, so the presented solution is just a different implementation of the same brute force algorithm, with 2 optimizations. The first optimization is using iterative implementation rather than recursive. I don't think it is significant because you are more likely to end up with out of time rather than out of stack space, but still it's a good one in general and not hard to implement. The most significant is the second one. The idea is, during the "forward" step, anytime the current sum becomes greater than the target sum, to be able to skip checking the next items that have greater or equal value to the current item. Usually that's accomplished by first sorting the input set, which is not applicable in your case. However, while thinking how to overcome that limitation, I realized that all I need is to have for each item the index of the first next item which value is less than the item value, so I can just jump to that index until I hit the end.
Now, although in the worst case both implementations perform the same way, i.e. may not end in a reasonable time, in many practical scenarios the optimized variant is able to produce result very quickly while the original still doesn't end in a reasonable time. You can check the difference by playing with maxQtys and maxQty parameters.
Here is the implementation described, with test code:
using System;
using System.Diagnostics;
using System.Linq;
namespace Tests
{
class Program
{
private static void Match(decimal[] inputQty, decimal matchSum, out int[] matchedIndices, out int matchCount, out int operations)
{
matchedIndices = new int[inputQty.Length];
matchCount = 0;
operations = 0;
var nextLessQtyPos = new int[inputQty.Length];
for (int i = inputQty.Length - 1; i >= 0; i--)
{
var currentQty = inputQty[i];
int nextPos = i + 1;
while (nextPos < inputQty.Length)
{
var nextQty = inputQty[nextPos];
int compare = nextQty.CompareTo(currentQty);
if (compare < 0) break;
nextPos = nextLessQtyPos[nextPos];
if (compare == 0) break;
}
nextLessQtyPos[i] = nextPos;
}
decimal currentSum = 0;
for (int nextPos = 0; ;)
{
if (nextPos < inputQty.Length)
{
// Forward
operations++;
var nextSum = currentSum + inputQty[nextPos];
int compare = nextSum.CompareTo(matchSum);
if (compare < 0)
{
matchedIndices[matchCount++] = nextPos;
currentSum = nextSum;
nextPos++;
}
else if (compare > 0)
{
nextPos = nextLessQtyPos[nextPos];
}
else
{
// Found
matchedIndices[matchCount++] = nextPos;
break;
}
}
else
{
// Backward
if (matchCount == 0) break;
var lastPos = matchedIndices[--matchCount];
currentSum -= inputQty[lastPos];
nextPos = lastPos + 1;
}
}
}
public class MatchSubset
{
private decimal[] qty = null;
private decimal matchSum = 0;
public int operations = 0;
public int[] matchedIndices = null;
public int matchCount = 0;
private bool SumUp(int i, int n, decimal sum)
{
operations++;
matchedIndices[matchCount++] = i;
sum += qty[i];
if (sum == matchSum)
return true;
if (i >= n - 1)
{
matchCount--;
return false;
}
if (SumUp(i + 1, n, sum))
return true;
sum -= qty[i];
matchCount--;
return SumUp(i + 1, n, sum);
}
public bool Match(decimal[] qty, decimal matchSum)
{
this.qty = qty;
this.matchSum = matchSum;
matchCount = 0;
matchedIndices = new int[qty.Count()];
return SumUp(0, qty.Count(), 0);
}
}
static void Main(string[] args)
{
int maxQtys = 3000;
decimal matchQty = 177820;
var qty = new decimal[maxQtys];
int maxQty = (int)(0.5m * matchQty);
var random = new Random();
for (int i = 0; i < maxQtys - 2; i++)
qty[i] = random.Next(1, maxQty);
qty[maxQtys - 2] = 99910;
qty[maxQtys - 1] = 77910;
Console.WriteLine("Source: {" + string.Join(", ", qty.Select(v => v.ToString())) + "}");
Console.WriteLine("Target: {" + matchQty + "}");
int[] matchedIndices;
int matchCount;
int operations;
var sw = new Stopwatch();
Console.Write("#1 processing...");
sw.Restart();
Match(qty, matchQty, out matchedIndices, out matchCount, out operations);
sw.Stop();
ShowResult(matchedIndices, matchCount, operations, sw.Elapsed);
Console.Write("#2 processing...");
var match = new MatchSubset();
sw.Restart();
match.Match(qty, matchQty);
sw.Stop();
ShowResult(match.matchedIndices, match.matchCount, match.operations, sw.Elapsed);
Console.Write("Done.");
Console.ReadLine();
}
static void ShowResult(int[] matchedIndices, int matchCount, int operations, TimeSpan time)
{
Console.WriteLine();
Console.WriteLine("Time: " + time);
Console.WriteLine("Operations: " + operations);
if (matchCount == 0)
Console.WriteLine("No Match.");
else
Console.WriteLine("Match: {" + string.Join(", ", Enumerable.Range(0, matchCount).Select(i => matchedIndices[i].ToString())) + "}");
}
}
}
Related
Basically I'm creating a program to randomly generate 6 unique lottery numbers so there is no duplicates in the same line, here is the code I have so far...
//Generate 6 random numbers using the randomiser object
int randomNumber1 = random.Next(1, 49);
int randomNumber2 = random.Next(1, 49);
int randomNumber3 = random.Next(1, 49);
int randomNumber4 = random.Next(1, 49);
int randomNumber5 = random.Next(1, 49);
int randomNumber6 = random.Next(1, 49);
textBox1.Text = randomNumber1.ToString();
textBox2.Text = randomNumber2.ToString();
textBox3.Text = randomNumber3.ToString();
textBox4.Text = randomNumber4.ToString();
textBox5.Text = randomNumber5.ToString();
textBox6.Text = randomNumber6.ToString();
}
I'm getting random numbers but sometimes there is the same number on the same line, how do I make each number unique????
Thanks in advance
You need to store them in a collection and each time you pick a new number you need to make sure it's not present already, otherwise you need to generate a new number until you find a unique number.
Instead of this, I would generate a sequence between 1 and 49, shuffle them and pick 6 number out of the sequence, for example:
var rnd = new Random();
var randomNumbers = Enumerable.Range(1,49).OrderBy(x => rnd.Next()).Take(6).ToList();
You can't. You've only specified that each number be a random number from 1 to 49, not that it shouldn't match any duplicates.
Since you've got a relatively small set of numbers, your best bet is probably to draw the random numbers, put them into a HashSet, then if you need more, pull more. Something like this:
HashSet<int> numbers = new HashSet<int>();
while (numbers.Count < 6) {
numbers.Add(random.Next(1, 49));
}
Here you're taking advantage of the HashSet's elimination of duplicates. This won't work with a List or other collection.
Returning repeat values is a necessity in order for a generator to satisfy a necessary statistical property of randomness: the probability of drawing a number is not dependent on the previous numbers drawn.
You could shuffle the integers in the range 1 to 49 and return the first 6 elements. See http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle for more details on such a shuffler.
However, I think you get a slight statistical bias by doing this.
The best way is probably to use random.Next(1, 49); and reject any repeat. That will be free from statistical bias and the fact that you're only wanting 6 from 49 possibilities, the number of collisions will not slow the algorithm appreciably.
Using this extension method for reservoir sampling:
public static IList<T> TakeRandom<T>(
this IEnumerable<T> source, int count, Random random)
{
var list = new List<T>(count);
int n = 1;
foreach (var item in source)
{
if (list.Count < count)
{
list.Add(item);
}
else
{
int j = random.Next(n);
if (j < count)
{
list[j] = item;
}
}
n++;
}
return list;
}
You can sample your collection like this:
var random = new Random();
var numbers = Enumerable.Range(1, 49).TakeRandom(6, random);
numbers.Shuffle(random);
Note the returned numbers will be uniformly sampled out of all (49 choose 6) possibilities for a set of 6 numbers out of {1, 2, ..., 49}, but they will neither remain in order nor be uniformly shuffled. If you want to have the order randomized as well, you can easily do a standard Fisher-Yates shuffle afterwards.
public static void Shuffle<T>(this IList<T> list, Random random)
{
for (int i = 0; i < list.Count; i++)
{
int j = random.Next(i, list.Count);
T temp = list[j];
list[j] = list[i];
list[i] = temp;
}
}
Note a more heavily optimized version of Fisher-Yates shuffle can be found in this answer: Randomize a List<T>
List<int> aux = new List<int>();
while(aux.Count < 6)
{
int rnd = random.Next(1,49);
if(!aux.Contains(rnd))aux.add(rnd);
}
if you put all Texbox in the same panel you can do that
int j = 0;
foreach(Control x in MyPanel.Controls)
{
if(x is TexBox)
{
x.Text = aux[j].toString();
j++;
}
}
It's my solution: generate array of number
/// <summary>
/// auto generate a array with number element and max value is max
/// </summary>
/// <param name="number">number element of array</param>
/// <param name="max">max value of array</param>
/// <returns>array of number</returns>
public static int[] createRandomArray(int number, int max)
{
List<int> ValueNumber = new List<int>();
for (int i = 0; i < max; i++)
ValueNumber.Add(i);
int[] arr = new int[number];
int count = 0;
while (count < number)
{
Random rd = new Random();
int index = rd.Next(0,ValueNumber.Count -1);
int auto = ValueNumber[index];
arr[count] = auto;
ValueNumber.RemoveAt(index);
count += 1;
}
return arr;
}
It's too late but I use a Method named M_Randomizer created by me. It may look as too much work, but it's technique is different from traditional which is based on generating a random number and checking the previously generated list for uniqueness. This code while generating a new random number, never looks for the previously generated. And if we talk about touching all combinations, I have tested this method till 9 factorial, maybe little bias for some but it touches all.
using System;
class Randomizer
{
public int[] M_Randomizer(int x)
{
bool b = false;
if (x < -1)
{
b = true;
x = -1 * x;
}
if(x == -1)
x = 0;
if (x < 2)
return new int[x];
int[] site;
int k = new Random(Guid.NewGuid().GetHashCode()).Next() % 2;
if (x == 2)
{
site = new int[2];
site[0] = k;
site[1] = 1 - site[0];
return site;
}
else if (x == 3)
{
site = new int[3];
site[0] = new Random(Guid.NewGuid().GetHashCode()).Next(0, 3);
site[1] = (site[0] + k + 1) % 3;
site[2] = 3 - (site[0] + site[1]);
return site;
}
site = new int[x];
int a = 0, m = 0, n = 0, tmp = 0;
int[] p = M_Randomizer(3);
int[] q;
if (x % 3 == 0)
q = M_Randomizer(x / 3);
else
q = M_Randomizer((x / 3) + 1);
if (k == 0)
{
for (m = 0; m < q.Length; m++)
{
for (n = 0; n < p.Length && a < x; n++)
{
tmp = (q[m] * 3) + p[n];
if (tmp < x)
{
site[a] = tmp;
a++;
}
}
}
}
else
{
while (n < p.Length)
{
while (a < x)
{
tmp = (q[m] * 3) + p[n];
if (tmp < x)
{
site[a] = tmp;
a++;
}
m = m + k;
if (m >= q.Length)
break;
}
m = m % q.Length;
n++;
}
}
a = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2) + 1;
k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
if (k > 5)
for (int i = a; i < k; i++)
while (a < site.Length)
{
if (k % (a + 1) == 0)
{
tmp = site[a - 1];
site[a - 1] = site[a];
site[a] = tmp;
}
a = a + 2;
}
k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
if (k > 5)
{
n = x / 2;
k = 0;
if (x % 2 != 0)
k = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2);
p = new int[n + k];
m = (x - n) - k;
for (a = 0; m < x; a++, m++)
p[a] = site[m];
m = n + k;
for (a = (x - m) - 1; a >= 0; a--, m++)
site[m] = site[a];
for (a = 0; a < p.Length; a++)
site[a] = p[a];
}
int[] site2;
int[] site3 = new int[x];
if (b)
return site;
else
site2 = M_Randomizer(-1 * x);
for (a = 0; a < site.Length; a++)
site3[site2[a]] = site[a];
return site3;
}
public int[] M_Randomizer(int x, int start)
{
int[] dm = M_Randomizer(x);
for(int a = 0; a < x; a++)
dm[a] = dm[a] + start;
return dm;
}
}
Look at using an array to hold your 6 numbers.
Each time you generate one, loop through the array to make sure it is not already there. If it is, then generate another & loop again until you have a non-match.
It's so easy with array and OOP (Object Oriented Programming). Before you start you have to add Linq (using System.Linq) library to your project.
Random random = new Random();
int[] array = new int[6];
int number;
for (int i = 0; i < 6; i++)
{
number = random.Next(1, 50);
if (!array.Contains(number)) //If it's not contains, add number to array;
array[i] = number;
else //If it contains, restart random process
i--;
}
for (int i = 1; i < 7; i++)
{
foreach (Control c in this.Controls) //Add random numbers to all Textboxes
{
if (c is TextBox && c.Name.EndsWith(i.ToString()))
{
c.Text = array[i - 1].ToString();
}
}
}
A functional approach could be to generate an infinite sequence of random numbers, filter out non-unique numbers and take the number of unique numbers you need.
For example:
private IEnumerable<int> RandomDigitStream(int seed)
{
Random random = new Random(seed);
while (true)
{
yield return random.Next(DIGIT_MIN, DIGIT_MAX);
}
}
private List<int> GenerateUniqueRandomNumbers(int seed, int count)
{
// Assert that DIGIT_MAX - DIGIT_MIN > count to ensure
// algorithm can finish
return RandomDigitStream(seed)
.Distinct()
.Take(count)
.ToList();
}
The efficiency of this algorithm is mainly dependent on how Distinct is implemented by the .NET team. Its memory usage would grow with the number of digits you require and the range of digits produced by the random function. It also has an unpredictable running time as it depends on the probability distribution of the random function. In fact it is possible for this algorithm to get stuck in an infinite loop if the range of digits produced by the random algorithm is less than the number of digits you require.
Looking at it practically however, it should be fine for a small amount of digits but if you are looking at a large number (100 +) you might want to look at other methods.
It would be more efficient to craft a random algorithm that only produces unique numbers in the first place if that is even possible without using a lookup table.
Here is a small program using recursion to generate number lines, and also uses recursion to randomize and get unique numbers.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static Random random;
public static List<int> lottoNumbers = Enumerable.Range(1, 49).ToList();
public static void Main()
{
random = new Random((int)DateTime.Now.Ticks);
var LinesToGenerate = 10;
GenerateNumbers(LinesToGenerate);
}
public static void GenerateNumbers(int LineCount)
{
int[] SelectedNumbers = new int[6];
for (var i = 0; i < 6; i++)
{
var number = GetRandomNumber(lottoNumbers.ToArray());
while (SelectedNumbers.Contains(number))
number = GetRandomNumber(lottoNumbers.ToArray());
SelectedNumbers[i] = number;
}
var numbersOrdered = SelectedNumbers.OrderBy(n => n).Select(n => n.ToString().PadLeft(2, '0'));
Console.WriteLine(string.Join(" ", numbersOrdered));
if (LineCount > 1)
GenerateNumbers(--LineCount);
}
//Recursively and randomly removes numbers from the array until only one is left, and returns it
public static int GetRandomNumber(int[] arr)
{
if (arr.Length > 1)
{
//Remove random number from array
var r = random.Next(0, arr.Length);
var list = arr.ToList();
list.RemoveAt(r);
return GetRandomNumber(list.ToArray());
}
return arr[0];
}
}
Yes. Use array.
Loop how many times you want:
Generate a random number,
Loop through array and compare all with the generated number.
If there's a match then loop again till there's no match.
Then store it.
Done:)
I have a problem with one exercise. Task is to find all subsets within the array which have a sum equal to N and print them. Also I need to find all unique subsets and this is the problem. I am using Gray method to find all combinations but some of them are duplicated. Here's my code:
int matchSum = int.Parse(Console.ReadLine());
int[] numbers = Console.ReadLine().Split().Select(int.Parse).ToArray();
int combinations = (int) Math.Pow(2, numbers.Length);
List<int> currentSequence = new List<int>();
bool foundMatch = false;
for (int i = 1; i < combinations; i++)
{
for (int bit = 0; bit < Convert.ToString(i,2).Length; bit++)
{
int mask = (i >> bit) & 1;
if (mask == 1)
{
currentSequence.Add(numbers[numbers.Length-bit-1]);
}
}
if (currentSequence.Sum() == matchSum)
{
Console.WriteLine("{0} = {1}", string.Join(" + ", currentSequence), matchSum);
foundMatch = true;
}
currentSequence.Clear();
}
if (!foundMatch)
{
Console.WriteLine("No matching subsets.");
}
Best regards!
Here is a backtracking algorithm implementation. It works by first sorting the input set and then producing the subsets and checking the sum. There are 3 key points. First, the algorithm maintains the current sum all the time so it doesn't need to fully calculate it at each step. Second, it early stops if the current sum becomes larger than the target sum. And finally, in order to produce unique subsets, the back step skips the numbers that are equal to the last number of the previous sequence. Hope that helps.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Samples
{
class Sample
{
static void Main(string[] args)
{
int matchSum = 20; // int.Parse(Console.ReadLine());
int[] numbers = { 5, 1, 3, 2, 5, 1, 8, 7, 4 }; // Console.ReadLine().Split().Select(int.Parse).ToArray();
Array.Sort(numbers);
var stack = new Stack<int>();
int matchCount = 0, currentSum = 0, nextPos = 0;
while (true)
{
// Next
for (int nextSum; nextPos < numbers.Length; currentSum = nextSum, nextPos++)
{
nextSum = currentSum + numbers[nextPos];
if (nextSum > matchSum) break;
stack.Push(nextPos);
if (nextSum < matchSum) continue;
matchCount++;
Console.WriteLine("{0} = {1}", matchSum, string.Join(" + ", stack.Reverse().Select(pos => numbers[pos])));
stack.Pop();
break;
}
// Back
if (stack.Count == 0) break;
var lastPos = stack.Pop();
var lastNumber = numbers[lastPos];
currentSum -= lastNumber;
nextPos = lastPos + 1;
while (nextPos < numbers.Length && numbers[nextPos] == lastNumber)
nextPos++;
}
if (matchCount == 0)
{
Console.WriteLine("No matching subsets.");
}
Console.ReadLine();
}
}
}
I found another way with a HashSet (thanks #Eric J.).
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static List<int> currentMatchList = new List<int>();
static void Main()
{
int matchSum = int.Parse(Console.ReadLine());
int[] numbers = Console.ReadLine().Split().Select(int.Parse).ToArray();
int combinations = (int)Math.Pow(2, numbers.Length);
List<int> currentSequence = new List<int>();
HashSet<string> allResults = new HashSet<string>();
bool foundMatch = false;
for (int i = 1; i < combinations; i++)
{
for (int bit = 0; bit < Convert.ToString(i, 2).Length; bit++)
{
int mask = (i >> bit) & 1;
if (mask == 1)
{
currentSequence.Add(numbers[numbers.Length - bit - 1]);
}
}
if (currentSequence.Sum() == matchSum)
{
string temp = "";
currentSequence.OrderBy(a => a).ToList().ForEach(a => temp += a + " ");
if (!allResults.Contains(temp))
{
allResults.Add(temp);
Console.WriteLine("{0} = {1}", string.Join(" + ", currentSequence), matchSum);
}
foundMatch = true;
}
currentSequence.Clear();
}
if (!foundMatch)
{
Console.WriteLine("No matching subsets.");
}
}
}
I'm having issues with my Mode and getting it to let me input more then 10 numbers!
Those are the two issues I am having with my code.
public static void Main(string[] args)
{
int[] myNums = new int[10];
int number = 0;
int count = 0;
Console.WriteLine("--Nmber Modifier--\n");
Console.WriteLine("Entering a number that is no between 1 and 10 will end your process");
do// limited to 10 HELP
{
number = Util.PromptForInt("Enter a number between 1 and 10 : ");
if ((number <= 10) && (number >= 1))
{
myNums[count] = number;
}
count++;
}
while ((number <= 10) && (number >= 1));
Array.Sort(myNums);
Console.WriteLine("Average number is : " + MeantAverage(myNums));
Console.WriteLine("Largest Number is : " + LargestNum(myNums));
Console.WriteLine("Smallest Number is : " + SmallestNum(myNums));
Console.WriteLine("Most common number is : " + Mode(myNums));
Console.ReadLine();
}
static double MeantAverage(int[] nums)
{
double dMeanAverage;
double dSum = 0;
var groups = nums.GroupBy(item => item);
foreach (var group in groups)
{
dSum = group.Key + dSum;
}
dMeanAverage = dSum / nums[nums.Length - 1];
return Math.Round(dMeanAverage, 2);
}
static int LargestNum(int[] nums)
{
int highestNum;
highestNum = nums[nums.Length - 1];
return highestNum;
}
static int SmallestNum(int[] nums)
{
int lowest = 0;
for (int b = 0; b < nums.Length; b++)
{
if (nums[b] > lowest)
{
lowest += nums[b];
return lowest;
}
} return lowest;
}
static int Mode(int[] nums)
{
// issues with mode
int modes = 0;
var modeGroup = nums.GroupBy(v => v);
int max = modeGroup.Max(g => g.Count());
modes = modeGroup.First(g => g.Count() == max).Key;
return modes;
}
}
}
You created an array of ten numbers:
int[] myNums = new int[10];
So while your loop doesn't restrict you to 10 numbers because you don't check against count, the system does because as soon as you try to access the 10th element (myNums[10]) you will get an IndexOutOfRangeException.
Since you don't catch it anywhere, its just going to terminate your program.
To solve your problem:
Check against count so you don't input too many numbers!
If you need a variable length collection, use a collection built for that like List<T> instead of an array. Arrays are fixed-length (mostly), and the way around that is a horrible misuse of the array semantic.
Modified code somewhat, intention is the same though.
You forgot to check against count in the while case
You will crash on '0' inputs (no safe guard against empty array)
Changed Mode to merge items and then sort descending
var myNums = new List<int>(10);
for(int i=0; i < 10; ++i)
{
int number = Utils.PromptForInt("Enter a number between 1 and 10 : ");
if(number > 10 || number < 1)
break;
myNums.Add(number);
}
if(myNums.Count < 1)
return; //no item input, do something
myNums.Sort();
Console.WriteLine("Average: {0}", myNums.Sum() / (double)myNums.Count);
Console.WriteLine("Largest: {0}", myNums[myNums.Count - 1]);
Console.WriteLine("Smallest: {0}", myNums[0]);
var result = myNums.GroupBy(n => n)
.Select(c => new { Key = c.Key, total = c.Count() })
.OrderByDescending( a => a.total);
Console.WriteLine("Most common: {0}, used {1} times", result.First().Key, result.First().total);
Is the following 0-1 Knapsack problem solvable:
'float' positive values and
'float' weights (can be positive or negative)
'float' capacity of the knapsack > 0
I have on average < 10 items, so I'm thinking of using a brute force implementation. However, I was wondering if there is a better way of doing it.
This is a relatively simple binary program.
I'd suggest brute force with pruning. If at any time you exceed the allowable weight, you don't need to try combinations of additional items, you can discard the whole tree.
Oh wait, you have negative weights? Include all negative weights always, then proceed as above for the positive weights. Or do the negative weight items also have negative value?
Include all negative weight items with positive value. Exclude all items with positive weight and negative value.
For negative weight items with negative value, subtract their weight (increasing the knapsack capavity) and use a pseudo-item which represents not taking that item. The pseudo-item will have positive weight and value. Proceed by brute force with pruning.
class Knapsack
{
double bestValue;
bool[] bestItems;
double[] itemValues;
double[] itemWeights;
double weightLimit;
void SolveRecursive( bool[] chosen, int depth, double currentWeight, double currentValue, double remainingValue )
{
if (currentWeight > weightLimit) return;
if (currentValue + remainingValue < bestValue) return;
if (depth == chosen.Length) {
bestValue = currentValue;
System.Array.Copy(chosen, bestItems, chosen.Length);
return;
}
remainingValue -= itemValues[depth];
chosen[depth] = false;
SolveRecursive(chosen, depth+1, currentWeight, currentValue, remainingValue);
chosen[depth] = true;
currentWeight += itemWeights[depth];
currentValue += itemValues[depth];
SolveRecursive(chosen, depth+1, currentWeight, currentValue, remainingValue);
}
public bool[] Solve()
{
var chosen = new bool[itemWeights.Length];
bestItems = new bool[itemWeights.Length];
bestValue = 0.0;
double totalValue = 0.0;
foreach (var v in itemValues) totalValue += v;
SolveRecursive(chosen, 0, 0.0, 0.0, totalValue);
return bestItems;
}
}
Yeah, brute force it. This is an NP-Complete problem, but that shouldn't matter because you will have less than 10 items. Brute forcing won't be problematic.
var size = 10;
var capacity = 0;
var permutations = 1024;
var repeat = 10000;
// Generate items
float[] items = new float[size];
float[] weights = new float[size];
Random rand = new Random();
for (int i = 0; i < size; i++)
{
items[i] = (float)rand.NextDouble();
weights[i] = (float)rand.NextDouble();
if (rand.Next(2) == 1)
{
weights[i] *= -1;
}
}
// solution
int bestPosition= -1;
Stopwatch sw = new Stopwatch();
sw.Start();
// for perf testing
//for (int r = 0; r < repeat; r++)
{
var bestValue = 0d;
// solve
for (int i = 0; i < permutations; i++)
{
var total = 0d;
var weight = 0d;
for (int j = 0; j < size; j++)
{
if (((i >> j) & 1) == 1)
{
total += items[j];
weight += weights[j];
}
}
if (weight <= capacity && total > bestValue)
{
bestPosition = i;
bestValue = total;
}
}
}
sw.Stop();
sw.Elapsed.ToString();
If you can only have positive values then every item with a negative weight must go in.
Then I guess you could calculate Value/Weight Ratio, and brute force the remaining combinations based on that order, once you get one that fits you can skip the rest.
The problem may be that the grading and sorting is actually more expensive than just doing all the calculations.
There will obviously be a different breakeven point based on the size and distribution of the set.
public class KnapSackSolver {
public static void main(String[] args) {
int N = Integer.parseInt(args[0]); // number of items
int W = Integer.parseInt(args[1]); // maximum weight of knapsack
int[] profit = new int[N + 1];
int[] weight = new int[N + 1];
// generate random instance, items 1..N
for (int n = 1; n <= N; n++) {
profit[n] = (int) (Math.random() * 1000);
weight[n] = (int) (Math.random() * W);
}
// opt[n][w] = max profit of packing items 1..n with weight limit w
// sol[n][w] = does opt solution to pack items 1..n with weight limit w
// include item n?
int[][] opt = new int[N + 1][W + 1];
boolean[][] sol = new boolean[N + 1][W + 1];
for (int n = 1; n <= N; n++) {
for (int w = 1; w <= W; w++) {
// don't take item n
int option1 = opt[n - 1][w];
// take item n
int option2 = Integer.MIN_VALUE;
if (weight[n] <= w)
option2 = profit[n] + opt[n - 1][w - weight[n]];
// select better of two options
opt[n][w] = Math.max(option1, option2);
sol[n][w] = (option2 > option1);
}
}
// determine which items to take
boolean[] take = new boolean[N + 1];
for (int n = N, w = W; n > 0; n--) {
if (sol[n][w]) {
take[n] = true;
w = w - weight[n];
} else {
take[n] = false;
}
}
// print results
System.out.println("item" + "\t" + "profit" + "\t" + "weight" + "\t"
+ "take");
for (int n = 1; n <= N; n++) {
System.out.println(n + "\t" + profit[n] + "\t" + weight[n] + "\t"
+ take[n]);
}
}
}
import java.util.*;
class Main{
static int max(inta,int b)
{
if(a>b)
return a;
else
return b;
}
public static void main(String args[])
{
int n,i,cap,j,t=2,w;
Scanner sc=new Scanner(System.in);
System.out.println("Enter the number of values ");
n=sc.nextInt();
int solution[]=new int[n];
System.out.println("Enter the capacity of the knapsack :- ");
cap=sc.nextInt();
int v[]=new int[n+1];
int wt[]=new int[n+1];
System.out.println("Enter the values ");
for(i=1;i<=n;i++)
{
v[i]=sc.nextInt();
}
System.out.println("Enter the weights ");
for(i=1;i<=n;i++)
{
wt[i]=sc.nextInt();
}
int knapsack[][]=new int[n+2][cap+1];
for(i=1;i<n+2;i++)
{
for(j=1;j<n+1;j++)
{
knapsack[i][j]=0;
}
}
/*for(i=1;i<n+2;i++)
{
for(j=wt[1]+1;j<cap+2;j++)
{
knapsack[i][j]=v[1];
}
}*/
int k;
for(i=1;i<n+1;i++)
{
for(j=1;j<cap+1;j++)
{
/*if(i==1||j==1)
{
knapsack[i][j]=0;
}*/
if(wt[i]>j)
{
knapsack[i][j]=knapsack[i-1][j];
}
else
{
knapsack[i][j]=max(knapsack[i-1][j],v[i]+knapsack[i-1][j-wt[i]]);
}
}
}
//for displaying the knapsack
for(i=0;i<n+1;i++)
{
for(j=0;j<cap+1;j++)
{
System.out.print(knapsack[i][j]+" ");
}
System.out.print("\n");
}
w=cap;k=n-1;
j=cap;
for(i=n;i>0;i--)
{
if(knapsack[i][j]!=knapsack[i-1][j])
{
j=w-wt[i];
w=j;
solution[k]=1;
System.out.println("k="+k);
k--;
}
else
{
solution[k]=0;
k--;
}
}
System.out.println("Solution for given knapsack is :- ");
for(i=0;i<n;i++)
{
System.out.print(solution[i]+", ");
}
System.out.print(" => "+knapsack[n][cap]);
}
}
This can be solved using Dynamic Programming. Below code can help you solve the 0/1 Knapsack problem using Dynamic Programming.
internal class knapsackProblem
{
private int[] weight;
private int[] profit;
private int capacity;
private int itemCount;
private int[,] data;
internal void GetMaxProfit()
{
ItemDetails();
data = new int[itemCount, capacity + 1];
for (int i = 1; i < itemCount; i++)
{
for (int j = 1; j < capacity + 1; j++)
{
int q = j - weight[i] >= 0 ? data[i - 1, j - weight[i]] + profit[i] : 0;
if (data[i - 1, j] > q)
{
data[i, j] = data[i - 1, j];
}
else
{
data[i, j] = q;
}
}
}
Console.WriteLine($"\nMax profit can be made : {data[itemCount-1, capacity]}");
IncludedItems();
}
private void ItemDetails()
{
Console.Write("\nEnter the count of items to be inserted : ");
itemCount = Convert.ToInt32(Console.ReadLine()) + 1;
Console.WriteLine();
weight = new int[itemCount];
profit = new int[itemCount];
for (int i = 1; i < itemCount; i++)
{
Console.Write($"Enter weight of item {i} : ");
weight[i] = Convert.ToInt32(Console.ReadLine());
Console.Write($"Enter the profit on the item {i} : ");
profit[i] = Convert.ToInt32(Console.ReadLine());
Console.WriteLine();
}
Console.Write("\nEnter the capacity of the knapsack : ");
capacity = Convert.ToInt32(Console.ReadLine());
}
private void IncludedItems()
{
int i = itemCount - 1;
int j = capacity;
while(i > 0)
{
if(data[i, j] == data[i - 1, j])
{
Console.WriteLine($"Item {i} : Not included");
i--;
}
else
{
Console.WriteLine($"Item {i} : Included");
j = j - weight[i];
i--;
}
}
}
}
I need to calculate the factorial of numbers up to around 100! in order to determine if a series of coin flip-style data is random, as per this Wikipedia entry on Bayesian probability. As you can see there, the necessary formula involves 3 factorial calculations (but, interestingly, two of those factorial calculations are calculated along the way to the third).
I saw this question here, but I'd think that integer is going to get blown out pretty quickly. I could also make a function that is more intelligent about the factorial calculation (ie, if I have 11!/(7!3!), as per the wiki example, I could go to (11*10*9*8)/3!), but that smacks of premature optimization to me, in the sense that I want it to work, but I don't care about speed (yet).
So what's a good C# library I can call to calculate the factorial in order to get that probability? I'm not interested in all the awesomeness that can go into factorial calculation, I just want the result in a way that I can manipulate it. There does not appear to be a factorial function in the Math namespace, hence the question.
You could try Math.NET - I haven't used that library, but they do list Factorial and Logarithmic Factorial.
There has been a previous question on a similar topic. Someone there linked the Fast Factorial Functions web site, which includes some explanations of efficient algorithms and even C# source code.
Do you want to calculate factorials, or binomial coefficients?
It sounds like you want to calculate binomial coefficients - especially as you mention 11!/(7!3!).
There may be a library that can do this for you, but as a (presumably) programmer visiting stack overflow there's no reason not to write one yourself. It's not too complicated.
To avoid memory overflow, don't evaluate the result until all common factors are removed.
This algorithm still needs to be improved, but you have the basis for a good algorithm here. The denominator values need to be split into their prime factors for the best result. As it stands, this will run for n = 50 quite quickly.
float CalculateBinomial(int n, int k)
{
var numerator = new List<int>();
var denominator = new List<int>();
var denominatorOld = new List<int>();
// again ignore the k! common terms
for (int i = k + 1; i <= n; i++)
numerator.Add(i);
for (int i = 1; i <= (n - k); i++)
{
denominator.AddRange(SplitIntoPrimeFactors(i));
}
// remove all common factors
int remainder;
for (int i = 0; i < numerator.Count(); i++)
{
for (int j = 0; j < denominator.Count()
&& numerator[i] >= denominator[j]; j++)
{
if (denominator[j] > 1)
{
int result = Math.DivRem(numerator[i], denominator[j], out remainder);
if (remainder == 0)
{
numerator[i] = result;
denominator[j] = 1;
}
}
}
}
float denominatorResult = 1;
float numeratorResult = 1;
denominator.RemoveAll(x => x == 1);
numerator.RemoveAll(x => x == 1);
denominator.ForEach(d => denominatorResult = denominatorResult * d);
numerator.ForEach(num => numeratorResult = numeratorResult * num);
return numeratorResult / denominatorResult;
}
static List<int> Primes = new List<int>() { 2, 3, 5, 7, 11, 13, 17, 19,
23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };
List<int> SplitIntoPrimeFactors(int x)
{
var results = new List<int>();
int remainder = 0;
int i = 0;
while (!Primes.Contains(x) && x != 1)
{
int result = Math.DivRem(x, Primes[i], out remainder);
if (remainder == 0)
{
results.Add(Primes[i]);
x = result;
i = 0;
}
else
{
i++;
}
}
results.Add(x);
return results;
}
I can estimate n = 110, k = 50 (returns 6x10^31) but cannot run n = 120, k = 50.
The following can calculate the factorial of 5000 in 1 second.
public class Number
{
#region Fields
private static long _valueDivision = 1000000000;
private static int _valueDivisionDigitCount = 9;
private static string _formatZeros = "000000000";
List<long> _value;
#endregion
#region Properties
public int ValueCount { get { return _value.Count; } }
public long ValueAsLong
{
get
{
return long.Parse(ToString());
}
set { SetValue(value.ToString()); }
}
#endregion
#region Constructors
public Number()
{
_value = new List<long>();
}
public Number(long value)
: this()
{
SetValue(value.ToString());
}
public Number(string value)
: this()
{
SetValue(value);
}
private Number(List<long> list)
{
_value = list;
}
#endregion
#region Public Methods
public void SetValue(string value)
{
_value.Clear();
bool finished = false;
while (!finished)
{
if (value.Length > _valueDivisionDigitCount)
{
_value.Add(long.Parse(value.Substring(value.Length - _valueDivisionDigitCount)));
value = value.Remove(value.Length - _valueDivisionDigitCount, _valueDivisionDigitCount);
}
else
{
_value.Add(long.Parse(value));
finished = true;
}
}
}
#endregion
#region Static Methods
public static Number operator +(Number c1, Number c2)
{
return Add(c1, c2);
}
public static Number operator *(Number c1, Number c2)
{
return Mul(c1, c2);
}
private static Number Add(Number value1, Number value2)
{
Number result = new Number();
int count = Math.Max(value1._value.Count, value2._value.Count);
long reminder = 0;
long firstValue, secondValue;
for (int i = 0; i < count; i++)
{
firstValue = 0;
secondValue = 0;
if (value1._value.Count > i)
{
firstValue = value1._value[i];
}
if (value2._value.Count > i)
{
secondValue = value2._value[i];
}
reminder += firstValue + secondValue;
result._value.Add(reminder % _valueDivision);
reminder /= _valueDivision;
}
while (reminder > 0)
{
result._value.Add(reminder % _valueDivision);
reminder /= _valueDivision;
}
return result;
}
private static Number Mul(Number value1, Number value2)
{
List<List<long>> values = new List<List<long>>();
for (int i = 0; i < value2._value.Count; i++)
{
values.Add(new List<long>());
long lastremain = 0;
for (int j = 0; j < value1._value.Count; j++)
{
values[i].Add(((value1._value[j] * value2._value[i] + lastremain) % _valueDivision));
lastremain = ((value1._value[j] * value2._value[i] + lastremain) / _valueDivision);
//result.Add(();
}
while (lastremain > 0)
{
values[i].Add((lastremain % _valueDivision));
lastremain /= _valueDivision;
}
}
List<long> result = new List<long>();
for (int i = 0; i < values.Count; i++)
{
for (int j = 0; j < i; j++)
{
values[i].Insert(0, 0);
}
}
int count = values.Select(list => list.Count).Max();
int index = 0;
long lastRemain = 0;
while (count > 0)
{
for (int i = 0; i < values.Count; i++)
{
if (values[i].Count > index)
lastRemain += values[i][index];
}
result.Add((lastRemain % _valueDivision));
lastRemain /= _valueDivision;
count -= 1;
index += 1;
}
while (lastRemain > 0)
{
result.Add((lastRemain % _valueDivision));
lastRemain /= _valueDivision;
}
return new Number(result);
}
#endregion
#region Overriden Methods Of Object
public override string ToString()
{
string result = string.Empty;
for (int i = 0; i < _value.Count; i++)
{
result = _value[i].ToString(_formatZeros) + result;
}
return result.TrimStart('0');
}
#endregion
}
class Program
{
static void Main(string[] args)
{
Number number1 = new Number(5000);
DateTime dateTime = DateTime.Now;
string s = Factorial(number1).ToString();
TimeSpan timeSpan = DateTime.Now - dateTime;
long sum = s.Select(c => (long) (c - '0')).Sum();
}
static Number Factorial(Number value)
{
if( value.ValueCount==1 && value.ValueAsLong==2)
{
return value;
}
return Factorial(new Number(value.ValueAsLong - 1)) * value;
}
}
using System;
//calculating factorial with recursion
namespace ConsoleApplication2
{
class Program
{
long fun(long a)
{
if (a <= 1)
{
return 1;}
else
{
long c = a * fun(a - 1);
return c;
}}
static void Main(string[] args)
{
Console.WriteLine("enter the number");
long num = Convert.ToInt64(Console.ReadLine());
Console.WriteLine(new Program().fun(num));
Console.ReadLine();
}
}
}
hello everybody according to this solution i have my own solution where i calculate factorial of array 1D elements. the code is `int[] array = new int[5]
{
4,3,4,3,8
};
int fac = 1;
int[] facs = new int[array.Length+1];
for (int i = 0; i < array.Length; i++)
{
for (int j = array[i]; j > 0; j--)
{
fac *= j;
}
facs[i] = fac;
textBox1.Text += facs[i].ToString() + " ";
fac = 1;
}`
copy and paste the code above ^ in the button , it solves factorial of elements of array 1D. best regards.