Dynamically calculate waiting time in a queue - c#

I have a method that calculates the waiting time in a queue. It works fine for a small range. However, you can quickly see that this would become very tedious to do with a large range. In this example, if you are #1 in the queue, your wait time is 'very soon'. If it's greater than 1 and less than 5: 0 to 1 weeks, and so on... How can I loop through this list to dynamically find the place in the queue?
if ((PlaceInQueue) <= 1)
{
result = "Very soon!";
}
if ((PlaceInQueue) > 1 & (PlaceInQueue) < 5)
{
result = "From 0 to 1 weeks.";
}
if ((PlaceInQueue) >= 5 & (PlaceInQueue) < 11)
{
result = "From 1 to 2 weeks.";
}
if ((PlaceInQueue) >= 11 & (PlaceInQueue) < 17)
{
result = "From 2 to 3 weeks.";
}
if ((PlaceInQueue) >= 17 & (PlaceInQueue) < 23)
{
result = "From 3 to 4 weeks.";
}
Here's what I've started and what I'm trying to accomplish. The first few if statements before the while loop may need to be hard coded as the math isn't exact and the rest would be dynamic. So, in this example, the results are correct up until the place in the queue is 11 or greater (inside the while loop).
int n = 1;
int max = 300; // Stop calculating when the max is reached
var PlaceInQue = (Convert.ToInt32(placeInQueue)); // This is the position in the Que
foreach (var item in PlaceInQue)
{
if (PlaceInQue <= 1)
{
result = "Very soon!";
}
if (PlaceInQue > 1 & PlaceInQue < 5)
{
result = "From 0 to 1 weeks.";
}
if (PlaceInQue >= 5 & PlaceInQue < 11)
{
result = "From 1 to 2 weeks.";
}
while (n < max)
{
if (PlaceInQue >= (should increment from 11 and then 17 then 23 then 29 and so on...) & PlaceInQue < (increment from 17 then 23 then 29 then 35 and so on...)
{
result = (should increment from "2 to 3 weeks" and then "3 to 4 weeks" and so on until max is reached...)
}
n++;
}
}

I think you need something like this:
if (PlaceInQue <= 1)
{
result = "Very soon!";
}
else if (PlaceInQue < 5)
{
result = "From 0 to 1 weeks.";
}
else if (PlaceInQue < 11)
{
result = "From 1 to 2 weeks.";
}
else if
{
for (int n = 11; n <= max; n += 5)
{
if (PlaceInQue >= n && PlaceInQue < n + 5)
{
int weeks = n / 5;
result = $"From {weeks} to {(weeks + 1)} weeks.";
break;
}
}
}

Below code works for me for which I have checked the probable edge cases. Let me know if it doesn't work for you.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
Queue<int> queue = new Queue<int>(Enumerable.Range(1, 300));
foreach (var placeInQueue in queue)
{
if(placeInQueue <= 1)
Console.WriteLine($"Place: {placeInQueue}. Very soon!");
else if(placeInQueue < 300)
{
var fromTo = GetFromTo(placeInQueue);
Console.WriteLine($"Place: {placeInQueue}. From {fromTo.Item1} to {fromTo.Item2} weeks.");
}
}
}
private static Tuple<int,int> GetFromTo(int place)
{
if (place < 5)
return new Tuple<int, int>(0, 1);
var q1 = place / 5;
var q2 = 0;
if((place + 1) % 6 == 0)
{
q2 = (place + 1) / 6;
q1 = int.MaxValue;
}
else
q2 = (place/6) == 0 ? q1 : (place/6);
var from = Math.Min(q1, q2);
var to = from + 1;
return new Tuple<int, int>(from, to);
}
}

Related

Is there a dynamic way to split 100 into different values?

I am stuck in a scenario which is complex for me to implement. Scenario is : I have rows in excel sheet which is dynamic . sometimes it could be 2, sometimes 5, sometimes 12 means any number of rows could be there. i need to assign different unique values to each available row in such a way that their sum should be equal to 100. Example : if i have 2 rows i can assign values 65 and 35 but not 50 to both as the value needs to be unique. similarly if i have 4 rows i need to assign values like 20,30, 27, 23 .Need a c# code for this.
How can i do this?
There are many ways you could generate different random numbers for each row. The way I generated the numbers was with DateTime.Now.Second and DateTime.Now.Minute. To ensure that the numbers never go over 100, you can subtract from the maximum number in the row. This is the general idea:
`
public static ulong CapAtNumber(ulong num, ulong cap) {
while(num>cap)
{
num = (num / 2) - 2;
if (num < 3)
{
num += (Convert.ToUInt64(DateTime.Now.Minute) + Convert.ToUInt64(DateTime.Now.Second) / 2);
}
}
return num;
}
This is an example of how you would find the maximum. I used the Math group to check for maximums among twoulong` numbers. The numbers are A B C D E F, and so on, and I group them into AB CD EF to find the maximum between the groups. The array size gets cut in half each time until the array size is 1. When the array size is 1, the number in the array is the maximum.
public static ulong[] FindTheMaxOfArray(ulong[] arg)
{
bool repeat = true;
ulong checknumber = 100;
while(repeat == true)
{
if (arg.Length % 2 == 0)
{
var halflist = new ulong[arg.Length / 2];
for (int z = 0; z < arg.Length / 2; z += 2)
{
halflist[z] = Math.Max(arg[z], arg[z + 1]);
if (
halflist.Length == 1)
{
repeat = true;
arg = new ulong[halflist.Length];
arg = halflist;
}
else { repeat = false;
checknumber = halflist[0]; }
}
}
else
{
var halflist = new ulong[arg.Length + 1 / 2];
for (int z = 0; z < halflist.Length; z++)
{
if (z % 2 != 0)
{
if (z != halflist.Length - 2)
{
halflist[z] = Convert.ToUInt64(Math.Max(Math.Max(arg[z], arg[z + 1]), arg[z + 2]));
}
else { halflist[z] = Convert.ToUInt64(Math.Max(arg[z], arg[z + 1])); }
}
}
if (halflist.Length == 1)
{
repeat = true;
arg = new ulong[halflist.Length];
arg = halflist;
}
else { checknumber = halflist[0];
repeat = false;
}
}
}
for(int z=0; z<arg.Length; z++)
{
if (arg[z] == checknumber)
{
arg[z] = CapAtNumber(arg[z], arg[z] - 20);
}
}
return arg;
}
if you want to ensure that there are no repeats, you can create a method to check for that. Each number in the foreach loop is checking with the numbers in the for loop. Since these are the same arrays, there has to be at least one repeat that happens naturally. Everything except this part seems to run smoothly. Here is an example
public static ulong[] Checkforrepeats(ulong[] args)
{
ulong repeatednumber = 1000;
int repeat = -1;
foreach( ulong num in args)
{
for(int z = 0; z<args.Length; z++)
{
if(z == 0)
{ repeat = -1; }
if(num ==args[z])
{
repeat += 1; //repeat was set to -1 to counter act the guaranteed repeat that will happen
}
if (repeat == 2)
{
args[z] += 2;
args[z - 1] -= 2;
}
}
}
return args;
}

C# encoding text RLE method

I'm trying to pack a given byte array by 'removing' repeated bytes, something like this:
Entrance 255 1 1 4 4 4 4 4 200 15 10
Output 1x255 2x1 5x4 1x200 1x15 1x10 => 255 1 1 5 4 200 15 10
If one byte is repeated more than 3 times I replace it with the counter.
I began with making a temporary byte list with no repeated values and list with numbers of appearances. I've got a problem with the counter though:
public static void compressBlock(List<byte> buffer)
{
byte marker = buffer.Last();
int counter = 1;
byte[] buffer_ar = new byte[buffer.Count];
buffer_ar = buffer.ToArray();
List<byte> temp = new List<byte>();
List<int> tmp = new List<int>();
int indeks = 0;
while (true)
{
if (buffer_ar[indeks] == buffer_ar[indeks + 1])
{
counter++;
if (buffer_ar[indeks] != buffer_ar[indeks + 1])
{
temp.Add(buffer_ar[indeks]);
tmp.Add(counter);
//counter = 1;
}
}
else
{
//counter = 1;
temp.Add(buffer_ar[indeks]);
tmp.Add(counter);
}
indeks++;
//counter = 1;
if (buffer_ar.Length -1 <= indeks) { break; }
}
As the output I have:
byte list: 255 1 4 200 15 10
int list: 1 2 6 6 6 6
I know I have to reset the counter at some point, but when I do that as the output of the int list I have: 1 1 1 1 1 1.
Could someone point me in the right direction to do that?
There are some issues with you implementation:
Decode is impossible since different inputs like 1 1 1 1 and 4 1 produce the same output: 4 1
What if the same items appears more than 255 (255 == Byte.MaxValue) times?
Better use general IEnumerable<Byte> then concrete List<Byte>
You don't need any buffer, just count the last item occurrence.
public static IEnumerable<Byte> RleEncode(IEnumerable<Byte> source)
{
if (null == source)
throw new ArgumentNullException("source");
const int threshold = 3;
Byte current = 0;
int count = 0;
foreach (var item in source)
if ((count == 0) || (current == item))
{
current = item;
count += 1;
}
else
{
if (count <= threshold)
for (int i = 0; i < count; ++i)
yield return current;
else
{
for (int i = 0; i < count / Byte.MaxValue; ++i)
{
yield return Byte.MaxValue;
yield return current;
}
if (count % Byte.MaxValue != 0)
{
yield return (Byte) (count % Byte.MaxValue);
yield return current;
}
}
current = item;
count = 1;
}
// Tail
if (count <= threshold)
for (int i = 0; i < count; ++i)
yield return current;
else
{
for (int i = 0; i < count / Byte.MaxValue; ++i)
{
yield return Byte.MaxValue;
yield return current;
}
if (count % Byte.MaxValue != 0)
{
yield return (Byte) (count % Byte.MaxValue);
yield return current;
}
}
}
Test
List<Byte> source = new List<Byte> {
255, 1, 1, 4, 4, 4, 4, 4, 200, 15, 10
};
// 255 1 1 5 4 200 15 10
String test = String.Join(" ", RleEncode(source));
You will never get here
if (buffer_ar[indeks] != buffer_ar[indeks + 1])
because it is placed inside the inverted if
if (buffer_ar[indeks] == buffer_ar[indeks + 1])
So you would never add the counter to your array

Pagination Math Issue

I have a pagination issue :
I have a large number of pages as results in my table ~50.000 pages and my pagination logic is something like :
1 2 3 ...50000
I would like to do something like :
1 2 3 10 100 500 1000 5000 10000 50000
And when i click for example 1111 :
1 1109 1110 1111 1112 1113 1114 1120 1200 1700 2200 3000 8000 10000 50000
so far I;ve tried something like this :
int Max_count = (int)Math.Floor(Math.Log10(totalPages) + 1);
for (int index = start; index <= end; index++)
{
if (index == pageNumber)
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.CurrentPage, pageNumber));
}
else if (index == start)
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.StartPage, 0));
}
else if (index == end)
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.EndPage, totalPages - 1));
}
else if ((index == start + 1) && (index > 1))
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.MorePages, -1));
}
else if ((index == end - 1) && (index < totalPages - 2))
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.MorePages, -1));
}
else if ((index > 100) && (index > totalPages / 2))
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.SimplePage, index));
}
else if ((pageNumber + 2 > index) && index + 10 < totalPages && !isSecond)
{
for (int temp = 1; temp < Max_count; temp++)
{
int power_var = (int)Math.Pow(10, temp);
int power_var_prev = (int)Math.Pow(10, temp - 1);
if ((pageNumber > power_var) || pageNumber < 10)
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.SimplePage, power_var - 1));
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.SimplePage, power_var - 1 + power_var_prev));
temp++;
}
}
isSecond = true;
}
else if (index - start < 3)
{
header.Items.Add(new PagingHeaderModelItem(PageHeaderItemType.SimplePage, index));
}
But i Feel like I'm not close to this at all. I'm not asking for a solution more of a hint or a formula I could use to do this by myself
LATER EDIT
The pattern should be like :
n-3, // 1 //1
n-2, // 2 //1109
n-1, // 3 //1110
**n**, // **4** //1111
n+1, //5 //1112
n+2, //6 //1113
n+3, //7 //1114
Math.Floor((index + 10)) / 10) * 10, //10 //1120
Math.Floor((index + 10)) / 10) * 10 +50, //60 //1170
Math.Floor((index + 100)) / 100) * 100, //100 //1200
Math.Floor((index + 100)) / 100) * 100+500,//600 //1700
Math.Floor((index + 1000)) / 1000) * 1000,//1000 //2000
Math.Floor((index + 1000)) / 1000) * 1000+5000,//6000/7000
....
Here's some code to print the desired values:
(it does print 1108 as well, but that should be easy enough to work around)
(changed +50, +500, etc. to +40, +400, etc. because I prefer that)
int min = 1, max = 50000;
if (val-3 > min)
Console.WriteLine(min);
for (int i = Math.Max(min, val-3); i <= Math.Min(max, val+3); i++)
Console.WriteLine(i);
int last = -1;
for (int i = 10; ; i *= 10)
{
int next = (val+3 + i) / i * i;
if (next > max)
break;
// prevent printing something like 90, 130, 100, 500 (100 won't print)
if (next > last)
Console.WriteLine(next);
next += 4*i;
if (next > max)
break;
Console.WriteLine(next);
last = next;
}
Live demo.
If you want to print it from the value to the minimum as well, it could be a simple case of copying the for-loop and inverting the values: (it will print the values from the largest, changing this will require a stack data structure)
for (int i = 10; ; i *= 10)
{
int next = (val-3 - i) / i * i;
if (next < min)
break;
if (next < last)
Console.WriteLine(next);
next -= 4*i;
if (next < min)
break;
Console.WriteLine(next);
last = next;
}
if (last != min)
Console.WriteLine(min);
Here's another idea:
Taking 1111 in 1..50000 as an example.
Take the 2 values before and the 2 values after - 1109, 1110, 1111, 1112, 1113.
Let's say we want an exponential growth towards the target, with 5 points in between.
The range of values upwards would be 50000 - 1113 = 48887 (starting from biggest value above).
Then we want to find x such that (5x)^2 = 48887. This is fairly easy to calculate, just square root 48887 and divide by 5 - sqrt(48887) / 5 = 44.22
Then the values would be:
1113 + (1 * 44.22) ^ 2 = 3068
1113 + (2 * 44.22) ^ 2 = 8934
1113 + (3 * 44.22) ^ 2 = 18712
1113 + (4 * 44.22) ^ 2 = 32400
1113 + (5 * 44.22) ^ 2 = 50000
Similarly for downwards.
You can probably base the number of values in between on how far the target is, if you wish.
If you'd prefer more round numbers, I'd have to think about that a bit more.

A recursion related issue in c#

This is the background to this question:
Background
Take any integer n greater than 1 and apply the following algorithm
If n is odd then n = n × 3 + 1 else n = n / 2
If n is equal to 1 then stop, otherwise go to step 1
The following demonstrates what happens when using a starting n of 6
6 - 3 - 10 - 5 - 16 - 8 - 4 - 2 - 1
After 8 generations of the algorithm we get to 1.
It is conjectured that for every number greater than 1 the repeated application of this algorithm will
eventually get to 1.
The question is how can I find a number that takes exactly 500 generations to reduce to 1?
The code below is my version but appearntly got some wrong logic. Could you help me correct this? Thanks in advance.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sequence1
{
class Program
{
static void Main(string[] args)
{
int start = 1;
int flag = 0;
int value;
while(true){
int temp = (start - 1) / 3;
string sta = temp.ToString();
if (Int32.TryParse(sta, out value) )
{
if (((start - 1) / 3) % 2 == 1)
{
start = (start - 1) / 3;
flag++;
if (flag == 500)
{
break;
}
}
else
{
start = start * 2;
flag++;
if (flag == 500)
{
break;
}
}
}
else
{
start = start * 2;
flag++;
if (flag == 500)
{
break;
}
}
}
Console.WriteLine("result is {0}", start);
Console.ReadLine();
}
}
}
Since your question's title is "A recursion related issue", I will give you a recursive solution.
int Process(int input, int maxRecursionDepth)
{
// condition to break recursion
if (maxRecursionDepth == 0 || input == 1)
return input;
if (input % 2 == 1) // odd case
return Process(input * 3 + 1, maxRecursionDepth - 1);
else // even case
return Process(input / 2, maxRecursionDepth - 1);
}
Now to find all number in a specified range, that return 1 after exactly 500 recursions:
int startRange = 1, endRange = 1000;
int maxDepth = 500;
List<int> resultList = new List<int>();
for (int i = startRange; i <= endRange; i++)
{
if (Process(i, maxDepth) == 1)
resultList.Add(i);
}
Your problem is a part of Collatz conjecture (about recursively defined function) which has not been solved yet:
http://en.wikipedia.org/wiki/Collatz_conjecture
so I think brute force is a good way out:
public static int GetMinNumber(int generations) {
if (generations < 0)
throw new ArgumentOutOfRangeException("generations");
// Memoization will be quite good here
// but since it takes about 1 second (on my computer) to solve the problem
// and it's a throwaway code (all you need is a number "1979515")
// I haven't done the memoization
for (int result = 1; ; ++result) {
int n = result;
int itterations = 0;
while (n != 1) {
n = (n % 2) == 0 ? n / 2 : 3 * n + 1;
itterations += 1;
if (itterations > generations)
break;
}
if (itterations == generations)
return result;
}
}
...
int test1 = GetMinNumber(8); // <- 6
int test2 = GetMinNumber(500); // <- 1979515
Observing the problem,
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
In the third iteration we hit the number 10, which is smaller than 13
So instead of calculating the sequence count every time we can use a cache.
static int GetMinCollatz(int maxChain)
{
const long number = 1000000;
int minNumber = 0;
// Temporary values
int tempCount = 0;
long temp = 0;
// Cache
int[] sequenceCache = new int[number + 1];
// Fill the array with -1
for (int index = 0; index < sequenceCache.Length; index++)
{
sequenceCache[index] = -1;
}
sequenceCache[1] = 1;
for (int index = 2; index <= number; index++)
{
tempCount = 0;
temp = index;
// If the number is repeated then we can find
// sequence count from cache
while (temp != 1 && temp >= index)
{
if (temp % 2 == 0)
temp = temp / 2;
else
temp = temp * 3 + 1;
tempCount++;
}
sequenceCache[index] = tempCount + sequenceCache[temp];
if (sequenceCache[index] == maxChain)
{
minNumber = index;
}
}
return minNumber;
}
For more details refer project euler and this.
A recursive solution
private void ReduceTo1(int input, ref int totalCount)
{
totalCount++;
if (input % 2 == 0)
{
input = input / 2;
}
else
{
input = input * 3 + 1;
}
if (input != 1)
ReduceTo1(input, ref totalCount);
}
to test
int desireValue = 0;
for (int i = 1; i < 100000; i++)
{
int totalCount = 0;
ReduceTo1(i, ref totalCount);
if (totalCount >= 500)
{
desireValue = i;
break;
}
}

C# 0-1 Knapsack Problem with known sum and number of zeros in set

I have a 5x5 table of values from 0 to 3 inclusive with all values unknown. I know both the sum of the values and the number of zeros for each row and column. How would I go about solving this 0-1 knapsack problem using C# and retrieving the possible solutions that satisfy the known sums and number of zeros? The tables will always be five rows and five columns, so it's not quite a traditional knapsack.
For example, say we input:
Row[0]: Sum=4, Zeros=1
[1]: Sum=5, Zeros=1
[2]: Sum=4, Zeros=2
[3]: Sum=8, Zeros=0
[4]: Sum=3, Zeros=2
Col[0]: Sum=5, Zeros=1
[1]: Sum=3, Zeros=2
[2]: Sum=4, Zeros=2
[3]: Sum=5, Zeros=1
[4]: Sum=7, Zeros=0
We would get this as a possible solution:
[[ 0 1 1 1 1 ]
[ 1 0 2 1 1 ]
[ 2 1 0 0 1 ]
[ 1 1 1 2 3 ]
[ 1 0 0 1 1 ]]
What type of algorithm should I employ in this rather strange situation? Would I also have to write a class just to enumerate the permutations?
Edit for clarification: the problem isn't that I can't enumerate the possibilities; it's that I have no clue how to go about efficiently determining the permutations adding to an arbitrary sum while containing the specified number of zeros and a maximum of 5 items.
Here there is the code. If you need any comment feel free to ask:
using System;
using System.Diagnostics;
namespace ConsoleApplication15
{
class Program
{
static void Main(string[] args)
{
RowOrCol[] rows = new RowOrCol[] {
new RowOrCol(4, 1),
new RowOrCol(5, 1),
new RowOrCol(4, 2),
new RowOrCol(8, 0),
new RowOrCol(3, 2),
};
RowOrCol[] cols = new RowOrCol[] {
new RowOrCol(5, 1),
new RowOrCol(3, 2),
new RowOrCol(4, 2),
new RowOrCol(5, 1),
new RowOrCol(7, 0),
};
int[,] table = new int[5, 5];
Stopwatch sw = Stopwatch.StartNew();
int solutions = Do(table, rows, cols, 0, 0);
sw.Stop();
Console.WriteLine();
Console.WriteLine("Found {0} solutions in {1}ms", solutions, sw.ElapsedMilliseconds);
Console.ReadKey();
}
public static int Do(int[,] table, RowOrCol[] rows, RowOrCol[] cols, int row, int col)
{
int solutions = 0;
int oldValueRowSum = rows[row].Sum;
int oldValueRowZero = rows[row].Zeros;
int oldValueColSum = cols[col].Sum;
int oldValueColZero = cols[col].Zeros;
int nextCol = col + 1;
int nextRow;
bool last = false;
if (nextCol == cols.Length)
{
nextCol = 0;
nextRow = row + 1;
if (nextRow == rows.Length)
{
last = true;
}
}
else
{
nextRow = row;
}
int i;
for (i = 0; i <= 3; i++)
{
table[row, col] = i;
if (i == 0)
{
rows[row].Zeros--;
cols[col].Zeros--;
if (rows[row].Zeros < 0)
{
continue;
}
if (cols[col].Zeros < 0)
{
continue;
}
}
else
{
if (i == 1)
{
rows[row].Zeros++;
cols[col].Zeros++;
}
rows[row].Sum--;
cols[col].Sum--;
if (rows[row].Sum < 0)
{
break;
}
else if (cols[col].Sum < 0)
{
break;
}
}
if (col == cols.Length - 1)
{
if (rows[row].Sum != 0 || rows[row].Zeros != 0)
{
continue;
}
}
if (row == rows.Length - 1)
{
if (cols[col].Sum != 0 || cols[col].Zeros != 0)
{
continue;
}
}
if (!last)
{
solutions += Do(table, rows, cols, nextRow, nextCol);
}
else
{
solutions++;
Console.WriteLine("Found solution:");
var sums = new int[cols.Length];
var zeross = new int[cols.Length];
for (int j = 0; j < rows.Length; j++)
{
int sum = 0;
int zeros = 0;
for (int k = 0; k < cols.Length; k++)
{
Console.Write("{0,2} ", table[j, k]);
if (table[j, k] == 0)
{
zeros++;
zeross[k]++;
}
else
{
sum += table[j, k];
sums[k] += table[j, k];
}
}
Console.WriteLine("| Sum {0,2} | Zeros {1}", sum, zeros);
Debug.Assert(sum == rows[j].OriginalSum);
Debug.Assert(zeros == rows[j].OriginalZeros);
}
Console.WriteLine("---------------");
for (int j = 0; j < cols.Length; j++)
{
Console.Write("{0,2} ", sums[j]);
Debug.Assert(sums[j] == cols[j].OriginalSum);
}
Console.WriteLine();
for (int j = 0; j < cols.Length; j++)
{
Console.Write("{0,2} ", zeross[j]);
Debug.Assert(zeross[j] == cols[j].OriginalZeros);
}
Console.WriteLine();
}
}
// The for cycle was broken at 0. We have to "readjust" the zeros.
if (i == 0)
{
rows[row].Zeros++;
cols[col].Zeros++;
}
// The for cycle exited "normally". i is too much big because the true last cycle was at 3.
if (i == 4)
{
i = 3;
}
// We readjust the sums.
rows[row].Sum += i;
cols[col].Sum += i;
Debug.Assert(oldValueRowSum == rows[row].Sum);
Debug.Assert(oldValueRowZero == rows[row].Zeros);
Debug.Assert(oldValueColSum == cols[col].Sum);
Debug.Assert(oldValueColZero == cols[col].Zeros);
return solutions;
}
}
public class RowOrCol
{
public readonly int OriginalSum;
public readonly int OriginalZeros;
public int Sum;
public int Zeros;
public RowOrCol(int sum, int zeros)
{
this.Sum = this.OriginalSum = sum;
this.Zeros = this.OriginalZeros = zeros;
}
}
}
How fast does it have to be? I just tested a naive "try pretty much anything" with some early aborts but less than would be possible, and it was pretty fast (less than a millisecond). It gave the solution:
[[ 0 1 1 1 1 ]
[ 1 0 1 1 2 ]
[ 1 0 0 1 2 ]
[ 2 1 2 2 1 ]
[ 1 1 0 0 1 ]]
If that's an acceptable solution to you, I can post the code (or just discuss it, it's quite verbose but the underlying idea is trivial)
edit: it is also trivially extendable to enumerating all solutions. It found 400 of them in 15 milliseconds, and claims that there are no more than that. Is that correct?
What I did, was start at 0,0 and try all values I could fill in at that place (0 though min(3, rowsum[0])), fill it it (subtracting it from rowsum[y] and colsum[x] and subtracting one from rowzero[y] and colzero[x] if the value was zero), then recursively do this for 0,1; 0,2; 0,3; then at 0,4 I have a special case where I just fill in the remaining rowsum if it is non-negative (otherwise, abort the current try - ie go up in the recursion tree), and something similar for when y=4. In the mean time, I abort when any rowsum colsum colzero or rowzero becomes negative.
The current board is a solution if and only if all remaining rowsums columnsums colzero's and rowzero's are zero. So I just test for that, and add it to the solutions if it is one. It won't have any negative entries by construction.

Categories