I'm trying to write knapsack c# algorithm with given conditions but there's always two problems i encountering. I'm getting "Index was outside the bounds of the array" error or my result is just 0.
I found couple code examples of Knapsack implementation and just can't figure out what I'm doing wrong.
Code examples:
https://www.programmingalgorithms.com/algorithm/knapsack-problem
http://www.csharpstar.com/csharp-knapsack-problem/
My code:
static int Knapsack(int n, int w, int[] s, int[] v)
{
int[,] G = new int[n+1,w+1];
for (int k = 0; k <= n; k++)
{
for (int r = 0; r < w; r++)
{
if (r == 0 || k == 0)
G[k, r] = 0;
else if (s[k] <= r)
G[k, r] = Math.Max(G[k- 1, r], v[k] + G[k - 1, r - s[k]]);
else
G[k, r] = G[k - 1, r];
}
}
return G[n, w];
}
static void Main(string[] args)
{
int[] s = { 60, 100, 120};
int[] v = { 10, 20, 30 };
int w = 50;
int n = s.Length;
Console.WriteLine(Knapsack(n, w, s, v));
}
In this case my result is 0.
The issue with your code is that s is the weights and v is the values and your weights of 60, 100, and 120 will obvious not fit into a capacity of 50, that's why you get a result of 0. The example you pull those values from set's the 60, 100, and 120 as the values and 10, 20, and 30 as the weights which is why it gets a result of 220.
I think this works better if you create a class to handle the related weight and value for the items.
public class Item
{
public int Weight { get; set; }
public int Value { get; set; }
}
Then the method only needs an array of items and the desired capacity. Also using meaningful names can make understanding what's going on easier than a bunch of single letter names.
public static int KnapSack(Item[] items, int capacity)
{
int[,] matrix = new int[items.Length + 1, capacity + 1];
for (int itemIndex = 0; itemIndex <= items.Length; itemIndex++)
{
// This adjusts the itemIndex to be 1 based instead of 0 based
// and in this case 0 is the initial state before an item is
// considered for the knapsack.
var currentItem = itemIndex == 0 ? null : items[itemIndex - 1];
for (int currentCapacity = 0; currentCapacity <= capacity; currentCapacity++)
{
// Set the first row and column of the matrix to all zeros
// This is the state before any items are added and when the
// potential capacity is zero the value would also be zero.
if (currentItem == null || currentCapacity == 0)
{
matrix[itemIndex, currentCapacity] = 0;
}
// If the current items weight is less than the current capacity
// then we should see if adding this item to the knapsack
// results in a greater value than what was determined for
// the previous item at this potential capacity.
else if (currentItem.Weight <= currentCapacity)
{
matrix[itemIndex, currentCapacity] = Math.Max(
currentItem.Value
+ matrix[itemIndex - 1, currentCapacity - currentItem.Weight],
matrix[itemIndex - 1, currentCapacity]);
}
// current item will not fit so just set the value to the
// what it was after handling the previous item.
else
{
matrix[itemIndex, currentCapacity] =
matrix[itemIndex - 1, currentCapacity];
}
}
}
// The solution should be the value determined after considering all
// items at all the intermediate potential capacities.
return matrix[items.Length, capacity];
}
Then running this code
var items = new[]
{
new Item {Value = 60, Weight = 10},
new Item {Value = 100, Weight = 20},
new Item {Value = 120, Weight = 30},
};
Console.WriteLine(KnapSack(items, 50));
results in 220.
Here's a solution that uses recursion.
public static int KnapSackRecursive(Item[] items, int capacity)
{
// If there are no items or capacity is 0 then return 0
if (items.Length == 0 || capacity == 0) return 0;
// If there is one item and it fits then return it's value
// otherwise return 0
if (items.Length == 1)
{
return items[0].Weight < capacity ? items[0].Value : 0;
}
// keep track of the best value seen.
int best = 0;
for (int i = 0; i < items.Length; i++)
{
// This is an array of the other items.
var otherItems = items.Take(i).Concat(items.Skip(i + 1)).ToArray();
// Calculate the best value without using the current item.
int without = KnapSackRecursive(otherItems, capacity);
int with = 0;
// If the current item fits then calculate the best value for
// a capacity less it's weight and with it removed from contention
// and add the current items value to that.
if (items[i].Weight <= capacity)
{
with = KnapSackRecursive(otherItems, capacity - items[i].Weight)
+ items[i].Value;
}
// The current best is the max of the with or without.
int currentBest = Math.Max(without, with);
// determine if the current best is the overall best.
if (currentBest > best)
best = currentBest;
}
return best;
}
Related
I'm playing around with different implementations for the most simple of the LeetCode problems TwoSums. I have various other ways working fine using methods like indexof, Dictionary searches, brute force, etc... and my best so far is better than 98.67% using List.IndexOf
I'm trying to implement a version with BinarySearch for comparison. It passes most tests but seems to fail when needing to sum duplicates in the first and last position and the list length is gt 2. I'm sure there are other fail conditions but I can't get past this one.
When stepping through the code it returns a negative number instead of the index when the last and first are the same value despite starting at i+1 index location.
I have to be missing something obvious but I'm just not seeing it for some reason. Maybe I'm just misunderstanding how BinarySearch works with index and length.
These pass:
{ 0, 2, 3 } target 3
result: {0, 2}
{ 3, 3 } target 6
result: { 0, 1 }
{ 0, 2, 0, 3 } target 0
result: { 0, 2 }
These fail:
{ 0, 2, 0 } target 0
expected: { 0, 2 }
result: null
{ 1, 4, 1 } target 2
expected: { 0, 2 }
result: null
The code sample is verbose for now while I'm working through the issues. I'll minimize it later.
offset is used to start the search at an index higher than i
subsetLength is used to keep the search length from going outside the bounds
The n > i is just a sanity check to make sure n is a higher index value than i before returning a valid result
public int[] TwoSum(int[] nums, int target)
{
List<int> numList = new List<int>(nums);
for (int i = 0; i < nums.Length; i++)
{
int offset = i + 1;
int subsetLength = nums.Length - offset;
int searchNum = target - nums[i];
int n = numList.BinarySearch(offset, subsetLength, searchNum, null);
if (n > i)
return new int[] { i, n };
}
return null;
}
Yes, you have a special case target / 2 + target / 2 == target when you check for two items each of which are target / 2:
public int[] TwoSum(int[] nums, int target) {
// Comment out if nums is already sorted
Array.Sort(nums);
for (int i = 0; i < nums.Length; ++i) {
int item = nums[i];
int toFind = target - item;
if (toFind < nums[i])
break;
int index = Array.BinarySearch(nums, toFind);
if (index >= 0) {
if (toFind == item) {
// Special case: two equal numbers: target / 2 + target / 2 = target
if (i < nums.Length - 1 && nums[i] == nums[i + 1])
return new int[] { i, i + 1 };
break;
}
else
return new int[] { i, index };
}
}
return new int[] { -1, -1 };
}
Just sort the list before searching it:
public int[] TwoSum(int[] nums, int target)
{
List<int> numList = new List<int>(nums);
numList.Sort();
for (int i = 0; i < nums.Length; i++)
{
int offset = i + 1;
int subsetLength = nums.Length - offset;
int searchNum = target - nums[i];
int n = numList.BinarySearch(offset, subsetLength, searchNum, null);
if (n > i)
return new int[] { i, n };
}
return null;
}
Code doesn't always find the index.
I'm trying to implement the fibonacci search algorithm in C#. Sometimes the algorithm doesn't find the element in the array. I wrote Unit Tests to check Code coverage and saw that 10 % of my code isn't reached. I also checked other implementations. I think the problem is this part:
if (GetFibonacciNumberOnIndex(index - 1) == 1 && hayStack[offset + 1] == needle)
return offset + 1;
But logically this should run when one last element is left.
public static int FibSearch(int[] hayStack, int needle)
{
if (hayStack == null)
{
throw new ArgumentNullException(nameof(hayStack), "The array containing values to search in mustn't be null");
}
if (hayStack.Length == 0)
{
return -1;
}
int index = 0;
while (GetFibonacciNumberOnIndex(index) < hayStack.Length)
index++;
int offset = -1;
while (GetFibonacciNumberOnIndex(index) > 1)
{
int i = Math.Min(offset + GetFibonacciNumberOnIndex(index - 2), hayStack.Length - 1);
if (needle < hayStack[i])
index -= 2;
else if (needle > hayStack[i])
{
index--;
offset = i;
}
else
return i;
}
if (GetFibonacciNumberOnIndex(index - 1) == needle && hayStack[offset + 1] == needle)
return offset + 1;
return -404;
}
private static int GetFibonacciNumberOnIndex(int index)
{
if (index < 1)
return 0;
else if (index == 1)
return 1;
int first = 0;
int second = 1;
int tmp = 0;
for (int i = 0; i < index; i++)
{
tmp = first;
first = second;
second = tmp + second;
}
return first;
}
}
I have a file with 1000000 numbers. I'm reading the file and converting the content to an integer array. Finally I'm searching for a number, knowing the number is in the array, but the algorithm returns -404. I verified that the number is in the array using Linq Extension Methods on the integer array.
I create a small sample with binary search. No advantage using Fabonacci search. You can use a streamreader to add the integers one at a time, they will get added and sorted at teh same time.
public class NumIndex:IComparable<NumIndex>
{
public int Number { get; set; }
public int Index { get; set; }
public int CompareTo(NumIndex other)
{
return this.Number.CompareTo(other.Number);
}
}
public static void Main(string[] args)
{
int[] nums = { 8, 4, 8, 1, 6, 3, 4, 32, 43, 5, 8, 95 };
List<NumIndex> intArray = new List<NumIndex>();
int i = 0;
foreach (int num in nums)
{
NumIndex newNum = new NumIndex() { Number = num, Index = i++ };
int index = intArray.BinarySearch(newNum);
if (index > -1)
{
//Item already found, you decide what to do with it, I added all numbers
}
else index = ~index;
intArray.Insert(index, newNum);
}
NumIndex dummy = new NumIndex() { Number = 18, Index = 0 };
int findItemIndex = intArray.BinarySearch(dummy);
if (findItemIndex < -1) throw new Exception("Item not found");
int itemIndex = intArray[].Index;
}
Okay, I know that this code is crude, and all around a messy, but I am no programmer, so bear with me. I have this code that lists a bunch of numbers, but I want it to not list any circular copies of the numbers.
For example, if the number 111262 is on my list, I don't want 112621, 126211, 262111, 621112, or 211126 to be listed.
Sorry, that number cannot be on the list.
For a true example, if the number 111252 is on my list, I don't want 112521, 125211, 252111, 521112, or 211125 to be listed.
Any help is appreciated!
namespace Toric_Classes
{
class Program
{
static void Main(string[] args)
{
int number_of_perms=0;
bool badsubsum1;
bool badsubsum2;
int subsum1 = 0;
int subsum2 = 0;
int sum = 0;
int class_length=6;
int[] toric_class=new int[class_length];
// The nested for loops scroll through every possible number of length class_length, where each digit can have a value of 1,2,..., or class_length-1
// Each number is looked at as an array, and is not stored anywhere, only printed if it satisfies certain conditions
for(int i1=1; i1<class_length; i1++)
{
toric_class[0] = i1;
for (int i2 = 1; i2 < class_length; i2++)
{
toric_class[1] = i2;
for (int i3 = 1; i3 < class_length; i3++)
{
toric_class[2] = i3;
for (int i4 = 1; i4 < class_length; i4++)
{
toric_class[3] = i4;
for (int i5 = 1; i5 < class_length; i5++)
{
toric_class[4] = i5;
for (int i6 = 1; i6 < class_length; i6++)
{
badsubsum1 = false;
badsubsum2 = false;
toric_class[5] = i6;
// Find the value of the sum of the digits of our array.
// We only want numbers that have a total digit sum being a multiple of class_length
for (int k = 0; k < class_length; k++)
{
sum += toric_class[k];
}
// The follwong two nested loops find the value of every contiguous subsum of our number, but not the total subsum.
// We *do not* want any subsum to be a multiple of class_length.
// That is, if our number is, say, 121342, we want to find 1+2, 1+2+1, 1+2+1+3, 1+2+1+3+4, 2+1, 2+1+3, 2+1+3+4, 2+1+3+4+2, 1+3, 1+3+4, 1+3+4+2, 3+4, 3+4+2, and 4+2
// The following checks 1+2, 1+2+1, 1+2+1+3, 1+2+1+3+4, 2+1, 2+1+3, 2+1+3+4, 1+3, 1+3+4, and 3+4
for (int i = 0; i < class_length - 1; i++)
{
for (int j = i + 1; j < class_length - 1; j++)
{
for (int k = i; k < j; k++)
{
subsum1 += toric_class[k];
}
if (subsum1 % class_length == 0)
{
badsubsum1 = true;
break;
}
subsum1 = 0;
}
}
// The following checks 2+1, 2+1+3, 2+1+3+4, 2+1+3+4+2, 1+3, 1+3+4, 1+3+4+2, 3+4, 3+4+2, and 4+2
for (int i = 1; i < class_length; i++)
{
for (int j = i + 1; j < class_length; j++)
{
for (int k = i; k < j; k++)
{
subsum2 += toric_class[k];
}
if (subsum2 % class_length == 0)
{
badsubsum2 = true;
break;
}
subsum2 = 0;
}
}
// We only want numbers that satisfies the following conditions
if (sum % class_length == 0 && badsubsum1 == false && badsubsum2 == false)
{
foreach (var item in toric_class)
{
Console.Write(item.ToString());
}
Console.Write(Environment.NewLine);
number_of_perms++;
}
sum = 0;
subsum1 = 0;
subsum2 = 0;
}
}
}
}
}
}
Console.WriteLine("Number of Permuatations: "+number_of_perms);
Console.Read();
}
}
}
EDIT
To clarify, I am creating a list of all numbers with length n that satisfy certain conditions. Consider the number d1d2...dn, where each di is a digit of our number. Each di may have value 1,2,...,n. Our number is in the list if it satisfies the following
The sum of all the digits is a multiple of n, that is,
d1+d2+...+dn = 0 mod n
Every contiguous subsum of the digits is not a multiple of n, aside from the total sum, that is, if i !=1 and j != n, then
di+d(i+1)+...+dj != 0 mod n
I should mention again that a "number" does not strictly use the numbers 0-9 in its digits. It may take any value between 1 and n. In my code, I am using the case where n=6.
The code works by creating an array of length class_length (in the code above, I use class_length=6). We first have 6 nested for loops that simply assign values to the array toric_class. The first for assigns toric_class[0], the second for assigns toric_class[1], and so on. In the first go around, we are generating the array 111111, then 111112, up to 111115, then 111121, etc. So essentially, we are looking at all heximal numbers that do not include 0. Once we reach our sixth value in our array, we check the array toric_class and check its values to ensure that it satisfies the above conditions. If it does, we simply print the array in a line, and move on.
Here is my easy and inefficient way that should work with minimal changes to your code. It requires shared string list var strList = new List<string>(); to store the used numbers. Then this part:
foreach (var item in toric_class)
{
Console.Write(item.ToString());
}
Console.Write(Environment.NewLine);
number_of_perms++;
becomes something like this:
string strItem = " " + string.Join(" ", toric_class) + " "; // Example: int[] {1, 12, 123} becomes " 1 12 123 "
if (!strList.Any(str => str.Contains(strItem))) // Example: if " 1 12 123 1 12 123 " contains " 1 12 123 "
{
Console.WriteLine(strItem);
strItem += strItem.Substring(1); // double the string, but keep only one space between them
strList.Add(strItem);
}
number_of_perms++; // not sure if this should be in the if statement
The idea is that for example the string " 1 1 1 2 5 2 1 1 1 2 5 2 " contains all circular copies of the numbers {1, 1, 1, 2, 5, 2}. I used string as a lazy way to check if array contains sub-array, but you can use similar approach to store copy of the used numbers in a list of arrays new List<int[]>() and check if any of the arrays in the list is circular copy of the current array, or even better HashSet<int[]>() approach similar to #slavanap's answer.
The first version of my answer was the easiest, but it works only with array of single digit items.
List is almost the same as array (new List<string>() instead of new string[]), but makes it much easier and efficient to add items to it. For example {1,2}.Add(3) becomes {1,2,3}.
str => str.Contains(strItem) is shortcut for a function that accepts parameter str and returns the result of str.Contains(strItem). That "function" is then passed to the .Any LINQ extension, so
strList.Any(str => str.Contains(strItem))
is shortcut for something like this:
foreach(string str in strList)
{
if (str.Contains(strItem))
{
return true;
}
}
return false;
The following method:
private static List<int> GetCircularEquivalents(int value)
{
var circularList = new List<int>();
var valueString = value.ToString();
var length = valueString.Length - 1;
for (var i = 0; i < length; i++)
{
valueString = valueString.Substring(1, length) + valueString.Substring(0, 1);
circularList.Add(int.Parse(valueString));
}
return circularList;
}
will return a list of the circular numbers derived from the input value. Using your example, this method can be called like this:
var circularList = GetCircularEquivalents(111262);
var dirtyList = new List<int> { 1, 112621, 2, 126211, 3, 262111, 4, 621112, 5, 211126, 6 };
var cleanList = dirtyList.Except(circularList).ToList();
which would result in a cleanList made up of the numbers 1 through 6, i.e. the dirtyList with all the circular numbers derived from 111262 removed.
That's where OOP really benefits. Comments inlined.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3 {
struct MyInt : IEquatable<MyInt> {
private int _value;
public MyInt(int value) {
_value = value;
}
// make it look like int
static public implicit operator MyInt(int value) {
return new MyInt(value);
}
public static explicit operator int(MyInt instance) {
return instance._value;
}
// main difference in these 3 methods
private int GetDigitsNum() {
int temp, res;
for (res = 0, temp = Math.Abs(_value); temp > 0; ++res, temp /= 10);
return res;
}
public bool Equals(MyInt other) {
int digits = other.GetDigitsNum();
if (digits != this.GetDigitsNum())
return false;
int temp = other._value;
// prepare mul used in shifts
int mul = 1;
for (int i = 0; i < digits - 1; ++i)
mul *= 10;
// compare
for (int i = 0; i < digits; ++i) {
if (temp == _value)
return true;
// ROR
int t = temp % 10;
temp = temp / 10 + t * mul;
}
return false;
}
public override int GetHashCode() {
// hash code must be equal for "equal" items,
// that's why use a sum of digits.
int sum = 0;
for (int temp = _value; temp > 0; temp /= 10)
sum += temp % 10;
return sum;
}
// be consistent
public override bool Equals(object obj) {
return (obj is MyInt) ? Equals((MyInt)obj) : false;
}
public override string ToString() {
return _value.ToString();
}
}
class Program {
static void Main(string[] args) {
List<MyInt> list = new List<MyInt> { 112621, 126211, 262111, 621112, 211126 };
// make a set of unique items from list
HashSet<MyInt> set = new HashSet<MyInt>(list);
// print that set
foreach(int item in set)
Console.WriteLine(item);
}
}
}
Output:
112621
I have a SortedList with long datatype elements where the elements
may repeat ( has some duplicate records too).
Eg: ValueCov = { 1,2,2,2,5,5,5,5,6,6,7,7,7,...........}
I have to search a value "x" each time in this list with the option
BinarySearch(x) in C# and when the value "x" is not there in the
ValueCov list, I need to find the next closer value's index. (least
diff from value "x" ).
The issue I have here is, as duplicate elements available if I search
for value x=3, This should return value=2's index from valueCov (
Index=1) . But the second time I search for value=3 this shouldn't
return the previously returned index because I'm fixing that
position for some other work. So I need the index as = 2 when I search
the second time.
Please provide a solution for this search while duplicates available in a list.
for (int i = 0; i < valueWat.Count; i++)
{
var x=valueWat[i];
int index = valueCov.BinarySearch(x);
//Console.Write(index + ",");
if (index >= 0)
{
//Console.Write(sortedCov[index].Key + ",");
Console.WriteLine("addr of w/m" + sortedWat[i].Key);
//adding addr of w/m and coveraddr
blockedWaterCoverMap.Add(sortedCov[index].Key, sortedWat[i].Key);
//valueCov.Remove(x);
//valueCov[index] = 10000000000;
valueCov[index] = -100000000;
// valueWat[i] = 20000000000;
//foreach (var z in blockedWaterCoverMap)
//{
// Console.WriteLine("cov,wat indexpair" + z.Key+z.Value);
//}
}
else
{
int ind = findClosest(x);
Console.WriteLine("index,value#index\t" + ind + "\t" + valueCov[ind]);
blockedWaterCoverMap.Add(sortedCov[ind].Key, sortedWat[i].Key);
valueCov[ind] = 00000; valueCov[index] = 00000;
}
}
/////////
private int findClosest(long data)
{
int i = 0; // index of currently checked element from valueCov
int ind =i; // returned index of the closest element
// current lowest distance to searched value:
long min = long.MaxValue;
// currently counted difference between input value
// and the next element of the list valueCov
long diff = 0;
var valueCov = new List<long>();
foreach (var y in sortedCov)
{
valueCov.Add(y.Value);
}
for ( i = 0; i < valueCov.Count; i++)
{
var x=valueCov[i];
if ((diff = Math.Abs(x - data)) < min)
{
min = diff;
ind = i; // the searched index is updated
//Console.WriteLine("findclosest i\t" + i);
}
}
// random selection of the index from the closest
// found values
List<int> indices = new List<int>();
for (int n = 0; n < valueCov.Count; n++)
{
if (valueCov[n] == valueCov[ind])
indices.Add(n);
}
Random r = new Random();
ind = indices[r.Next(indices.Count)];
return ind;
}
I would just use linq to get the value, and you can later get the index by BinarySearch
public static void Main()
{
var valueCov = new long[]{ 1,2,2,2,5,5,5,5,6,6,7,7,7};
var result = 0L;
var x = 3;
result = FindNextCloseset(valueCov, x, result);
Console.WriteLine(result);
result = FindNextCloseset(valueCov, x, result);
Console.WriteLine(result);
}
public static long FindNextCloseset(long[] values, int occurrence, long searchAfterValue)
{
return values
.Where(i => i > searchAfterValue)
.GroupBy(i => i)
.Where(i => i.Count() == occurrence)
.Select(i => i.Key)
.FirstOrDefault();
}
Fiddle
Here is a solution that I came up with that you can start with. It is long, and you should create unit tests and refactor it since it contains a lot of logic:
public static int FindIndexOfClosest(List<long> list, long value_to_search_for, int time)
{
int index = list.BinarySearch(value_to_search_for);
if (index >= 0) //We found the value
{
while (index > 0 && list[index - 1] == value_to_search_for)
//BinarySearch might not return the index of the first occurance
{
index--;
}
index += time;
if (index >= list.Count || list[index] != value_to_search_for)
return -1;
return index;
}
else
{
int location_for_next_larger_number = ~index; //This could be equal to Count
int? larger_index = location_for_next_larger_number == list.Count
? (int?) null
: location_for_next_larger_number;
int? smaller_index = null;
if (!larger_index.HasValue)
{
if (list.Count > 0)
smaller_index = list.Count - 1;
}
else
{
int i = location_for_next_larger_number;
while (i > 0 && list[i - 1] == larger_index.Value)
i--;
if (i > 0)
smaller_index = i - 1;
}
int? closer_number_index = null;
if (larger_index.HasValue)
closer_number_index = larger_index.Value;
if (smaller_index.HasValue)
{
if (!closer_number_index.HasValue)
closer_number_index = smaller_index.Value;
else
{
if (Math.Abs(list[smaller_index.Value] - value_to_search_for) < Math.Abs(list[closer_number_index.Value] - value_to_search_for))
closer_number_index = smaller_index.Value;
}
}
if (closer_number_index.HasValue)
{
while (closer_number_index > 0 && list[closer_number_index.Value - 1] == list[closer_number_index.Value])
closer_number_index = closer_number_index.Value - 1;
long closer_number_value = list[closer_number_index.Value];
closer_number_index += time;
if (closer_number_index.Value >= list.Count || list[closer_number_index.Value] != closer_number_value)
return -1;
return closer_number_index.Value;
}
return -1;
}
}
And you can test it like this:
static void Main(string[] args)
{
List<long> list = new List<long> {1, 2, 2, 2, 5, 5, 5, 5, 6, 6, 7, 7, 7};
var test1 = FindIndexOfClosest(list, 3, 0); // returns 1
var test2 = FindIndexOfClosest(list, 3, 1); // returns 2
var test3 = FindIndexOfClosest(list, 3, 2); // returns 3
var test4 = FindIndexOfClosest(list, 3, 3); // returns -1 (not found)
}
Make sure that the list that you give to the FindIndexOfClosest method is always sorted.
Take a look at the reference for the BinarySearch method as it will help you understand the code I provided.
For example i have and array with such elements:
0 21 29 0 0 50
let's "flat" this numbers:
let's say my random number is 54, so my number belongs to the last element in my array with index 6 (it's 50)
i can't understand, how to algorytmize that... with c#
i try:
Random random = new Random();
int randomNumber = random.Next(1, 100);
int temp, temp_ind;
float a_, b_;
for (j = 0; j < n-1; j++)
{
if (roulette[j] != 0)
{
temp_ind = j+1;
a_ = roulette[j];
while ((roulette[temp_ind] == 0.0) && (temp_ind < n-1))
{
temp_ind++;
}
b_ = roulette[temp_ind];
if ((a_ <= randomNumber) && (b_ >= randomNumber))
{
start = j;
break;
}
}
}
but this doesn't work, maybe something could help me?
Here's a solution which converts the array to a cumulative array (using an extension method from this answer by Eric Lippert), then finds the index of the first match in that array which is higher than the random number.
class Program
{
static void Main(string[] args)
{
var random = new Random();
int[] roulette = { 0, 21, 29, 0, 50 };
var cumulated = roulette.CumulativeSum().Select((i, index) => new { i, index });
var randomNumber = random.Next(0, 100);
var matchIndex = cumulated.First(j => j.i > randomNumber).index;
Console.WriteLine(roulette[matchIndex]);
}
}
public static class SumExtensions
{
public static IEnumerable<int> CumulativeSum(this IEnumerable<int> sequence)
{
int sum = 0;
foreach (var item in sequence)
{
sum += item;
yield return sum;
}
}
}
You have hopelessly too many variables, overcomplicating the problem. Beyond the counter and the number, you only need one additional variable to keep track of the closest smaller number.
Below is some code I wrote which has essentially the same idea, it just seems a bit simpler.
int[] roulette = {0, 21, 29, 0, 0, 50};
int closest = -1;
int number = 54;
for (int j = 0; j < roulette.Length; j++)
// if the values isn't 0 and it's smaller
// and we haven't found a smaller one yet, or this one's closer
if (roulette[j] != 0 && roulette[j] < number &&
(closest == -1 || roulette[j] > roulette[closest]))
{
closest = j;
}
if (closest == -1) // no smaller number found
Console.WriteLine(0);
else
Console.WriteLine(roulette[closest]);
Live demo.
For repeated queries, it would be better to sort the numbers, then do a binary search to find the correct position.