C# encoding text RLE method - c#

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

Related

How to get integers to an array (from integer i to integer j). c#

I want to store integers(in an array or anything) that in range of int "i" and int "j".
eg:-Think, "int i = 1" and "int j = 10".I want to store integers from 1 and 10.
So that (1,2,3,4,5,6,7,8,9,10)
Because I want to answer to HackerRank "Beautiful Days at the Movies".
link below.
https://www.hackerrank.com/challenges/beautiful-days-at-the-movies/problem?isFullScreen=false
here is my code and it a garbage.
static int beautifulDays(int i, int j, int k) {
var total = 0;
for(var a = i; a <= j; a++ )
{
if (a != 0)
{
int ri = Reverse(i);
int rj = Reverse(j);
var ra = Reverse(a);
if((ra/k) % 1 == 0)
{
total++;
}
if((rj/k) % 1 == 0)
{
total++;
}
if((ri/k) % 1 == 0)
{
total++;
}
}
return total;
}
return total;
}
public static int Reverse(int inval)
{
int result = 0;
do
{
result = (result * 10) + (inval % 10);
inval = inval / 10;
}
while(inval > 0);
return result;
}
simply, can you give me the answer of HackerRank "Beautiful Days at the Movies".
link below.
https://www.hackerrank.com/challenges/beautiful-days-at-the-movies/problem?isFullScreen=false
Using Java you can easily stream a range of numbers with IntStream, then map the reverse function for each value, then filter those that fulfils the condition and count. With streams you don't need to store, you can get straight to the answer.
IntUnaryOperator reverse = (opperand) -> {
int reversed = 0;
int num = opperand;
while (num != 0) {
int digit = num % 10;
reversed = reversed * 10 + digit;
num /= 10;
}
return Math.abs(opperand - reversed);
};
return (int) IntStream.rangeClosed(i, j).map(reverse)
.filter(v -> v % k == 0).count();

C#, one-dimension wrapping array

I have a one-dimensional array, and I need to run operations on it based on the adjacents cells of every cell.
For instance:
To run the operations on the first cell, I'll need to access the last cell and the second.
The second cell, I'll need to access the first cell and the third cell.
The last cell, I'll need to access the first cell and the one before the last cell.
My code so far is:
public static int[] firstRule(int[] numberArray)
{
for (int i = 0; i < numberArray.Length; i++)
{
if (numberArray[numberArray.Length - 1 - i] == numberArray[i] + 1
&& numberArray[i + 1] == numberArray[i] + 1)
{
numberArray[i] = numberArray[i] - 1;
}
}
return numberArray;
}
But the problem with my approach is that it would only work for the first cell, as it would take the last cell and the second cell correctly, but after that, everything would fall apart. I don't like posting without a lot of code but I have no clue how to follow this up.
I am trying to achieve the following:
Those values are numbers ranging from 0 to 3. If both the cell before and the cell after is the same number, I want to change it to x + 1
For instance: suppose I have 1 0 1 2 2. I would want to change 0 to 1. As the neighbor cells are both 0.
Just keep it simple and use variables to calculate the left and right cell indices. Inside your for loop you can do this...
var leftCell = i - 1;
if (leftCell < 0)
{
leftCell = numberArray.Length - 1; // Wrap around to the end...
}
var rightCell = i + 1;
if (rightCell > numberArray.Length - 1)
{
rightCell = 0; // Wrap back around to the beginning...
}
// Now you can update your original code to use these computed indices...
if (numberArray[leftCell] == numberArray[i] + 1
&& numberArray[rightCell] == numberArray[i] + 1)
{
numberArray[i] = numberArray[i] - 1;
}
Try this out:
var len = numberArray.Length;
for (int i = 0; i < len; i++)
{
var leftIndex = (i - 1 + len) % len;
var rightIndex = (i + 1) % len;
// do your stuff with numberArray[leftIndex] and numberArray[rightIndex]
}
% is mod operator. % len allows you to stay in range 0..len-1, so you can walk through array as if it has become 'cyclic'
From your comments.
Those values are numbers ranging from 0 to 3. If both the cell before and the cell after is the same number, I want to change it to x + 1
For instance: suppose I have 1 0 1 2 2. I would want to change 0 to 1. As the neighbor cells are both 0.
I would create a new array, populate it with the values of the existing array and then change the values of the new array according to the results of the value in the existing array.
Edit as Op is getting wrong values
I suspect you may not be copying the array correctly instead:
Existing Array array // The array you are passing in as parameter.
Declare a new empty array:
int[] newArray;
int size = array.length;
for(int i =1; i<size-1;i++){
if(array[i-1]==array[i+1])){
newArray[i]=array[i]+1;
}
else{
newArray[i]=array[i];
}
}
if(array[size-1]==array[0]){
newArray[size]= array[size]+1;
}
else{
newArray[i]=array[i];
}
if(array [size]==array[1]){
newArray[0]= array[0]+1;
}
else{
newArray[i]=array[i];
}
if there is a limit to the number and it reverts to zero after 2, then just do a simple if test for that.
int[] arr = new int[] { 1, 2, 3, 4, 5 };
var triples = arr.Select((n, i) =>
{
if (i == 0)
return Tuple.Create(arr[arr.Length - 1], arr[0], arr[1]);
else if (i == arr.Length - 1)
return Tuple.Create(arr[i - 1], arr[i], arr[0]);
else
return Tuple.Create(arr[i - 1], arr[i], arr[i + 1]);
});
foreach (var triple in triples)
{
Console.WriteLine(triple.Item1 + " " + triple.Item2 + " " + triple.Item3);
}
public static void firstRule(int[] numberArray)
{
for (int i = 0; i < numberArray.Length; i++)
{
int? prevElement = i == 0
? numberArray[numberArray.Length-1]
: numberArray[i - 1];
int? nextElement = i == numberArray.Length -1
? numberArray[0]
: numberArray[i + 1];
Console.WriteLine(
String.Format("Prev: {0}; Current: {1}; Next: {2}",
prevElement,
numberArray[i],
nextElement)
);
}
}
And then calling firstRule(new int[]{ 1, 2, 3 }); prints:
Prev: 3; Current: 1; Next: 2
Prev: 1; Current: 2; Next: 3
Prev: 2; Current: 3; Next: 1
OPTION 1
assign regardless
public static int[] firstRule(int[] numberArray)
{
int left,right;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
left = (i == 0) ? max : i - 1;
right = (i == max) ? 0 : i + 1;
numberArray[i] = (numberArray[left] == numberArray[right]) ? numberArray[i] + 1 : numberArray[i]; //always peforms an assignment;
}
return numberArray;
}
OPTION 2
conditionally assign
public static int[] secondRule(int[] numberArray)
{
int left,right;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
left = (i == 0) ? max : i - 1;
right = (i == max) ? 0 : i + 1;
if (numberArray[left] == numberArray[right])
{
numberArray[i]++;
}
}
return numberArray;
}
OPTION 3
left and right are only used 1 time in each iteration.. so why bother assigning them to a variable???...
public static int[] thirdRule(int[] numberArray)
{
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
if (numberArray[(i == 0) ? max : i - 1] == numberArray[(i == max) ? 0 : i + 1])
{
numberArray[i]++; // what happens if numberArray[i] is 3, should it become 4 or 0?
}
}
return numberArray;
}
OPTION 4 (UNSAFE)
unsafe - fixed - pointers
public static int[] fourthRule(int[] numberArray)
{
unsafe {
int* pointer, right, left;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
fixed (int* p1 = &numberArray[0], p2 = &numberArray[i], p3 = &numberArray[max])
{
pointer = p2;
if (i == 0)
{
left = p3;
right = pointer;
right++;
}
else if (i == max)
{
left = pointer;
left--;
right = p1;
}
else
{
left = pointer;
left--;
right = pointer;
right++;
}
if (*right == *left) {
*pointer = *pointer + 1;
}
}
}
}
return numberArray;
}
Recently came up against this myself and found this to be a solid method.
`
int length = numberArray.Length;
for (int i = 0; i < length; ++i)
{
int iMinus = (((i - 1) % length) + length) % length;
int iPlus = (((i + 1) % length) + length) % length;
}`
Something like this should work. It determines the appropriate cells for the operation in each loop and executes the operation. You didn't state what that operation was so you need to fill in the DoYourOperation method.
public static int[] processArray(int[] numberArray)
{
for (int i= 0; i < numberArray.Length; i++)
{
int firstCell;
int secondCell;
//Check if first cell
if(i == 0)
{
firstCell = numberArray[numberArray.length-1]; //Last cell
secondCell = numberArray[i++]; //Next cell
}
//Check if last cell
else if(i == numberArray.Length - 1)
{
firstCell = numberArray[i--]; //Cell before last one
secondCell = numberArray[0]; //First cell
}
else
{
firstCell = numberArray[i--];
secondCell = numberArray[i++];
}
DoYourOperation(firstCell, secondCell);
}
}

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;
}
}

Counting alternating numbers in an array

Given an array of integers...
var numbers = new int[] { 1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,2,1,2,1 };
I need to determine a the maximum sequence of numbers that alternate up then down or down then up.
Not sure the best way to approach this, the process psuedo wise strikes me as simple but materializing code for it is evading me.
The key is the fact we are looking for max sequence, so while the above numbers could be interpreted in many ways, like a sequence of seven up-down-up and seven down-up-down the important fact is starting with the first number there is a down-up-down sequence that is 14 long.
Also I should not that we count the first item, 121 is a sequence of length 3, one could argue the sequence doesn't begin until the second digit but lets not split hairs.
This seems to work, it assumes that the length of numbers is greater than 4 (that case should be trivial anyways):
var numbers = new int[] { 1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,2,1,2,1 };
int count = 2, max = 0;
for (int i = 1; i < numbers.Length - 1; i++)
{
if ((numbers[i - 1] < numbers[i] && numbers[i + 1] < numbers[i]) ||
(numbers[i - 1] > numbers[i] && numbers[i + 1] > numbers[i]))
{
count++;
max = Math.Max(count, max);
}
else if ((numbers[i - 1] < numbers[i]) || (numbers[i - 1] > numbers[i])
|| ((numbers[i] < numbers[i + 1]) || (numbers[i] > numbers[i + 1])))
{
max = Math.Max(max, 2);
count = 2;
}
}
Console.WriteLine(max); // 14
Here's how I thought of it
First, you need to know whether you're starting high or starting low. eg: 1-2-1 or 2-1-2. You might not even have an alternating pair.
Then, you consider each number afterwards to see if it belongs in the sequence, taking into consideration the current direction.
Everytime the sequence breaks, you need to start again by checking the direction.
I am not sure if it is possible that out of the numbers you have already seen, picking a different starting number can POSSIBLY generate a longer sequence. Maybe there is a theorem that shows it is not possible; maybe it is obvious and I am over-thinking. But I don't think it is possible since the reason why a sequence is broken is because you have two high's or two low's and there is no way around this.
I assumed the following cases
{} - no elements, returns 0
{1} - single element, returns 0
{1, 1, 1} - no alternating sequence, returns 0
No restriction on the input beyond what C# expects. It could probably be condensed. Not sure if there is a way to capture the direction-change logic without explicitly keeping track of the direction.
static int max_alternate(int[] numbers)
{
int maxCount = 0;
int count = 0;
int dir = 0; // whether we're going up or down
for (int j = 1; j < numbers.Length; j++)
{
// don't know direction yet
if (dir == 0)
{
if (numbers[j] > numbers[j-1])
{
count += 2; // include first number
dir = 1; // start low, up to high
}
else if (numbers[j] < numbers[j-1])
{
count += 2;
dir = -1; // start high, down to low
}
}
else
{
if (dir == -1 && numbers[j] > numbers[j-1])
{
count += 1;
dir = 1; // up to high
}
else if (dir == 1 && numbers[j] < numbers[j-1])
{
count += 1;
dir = -1; // down to low
}
else
{
// sequence broken
if (count > maxCount)
{
maxCount = count;
}
count = 0;
dir = 0;
}
}
}
// final check after loop is done
if (count > maxCount)
{
maxCount = count;
}
return maxCount;
}
And some test cases with results based on my understanding of the question and some assumptions.
static void Main(string[] args)
{
int[] nums = { 1}; // base case == 0
int[] nums2 = { 2, 1 }; // even case == 2
int[] nums3 = { 1, 2, 1 }; // odd case == 3
int[] nums4 = { 2, 1, 2 }; // flipped starting == 3
int[] nums5 = { 2, 1, 2, 2, 1, 2, 1 }; // broken seqeuence == 4
int[] nums6 = { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1 }; // long sequence == 14
Console.WriteLine(max_alternate(nums));
Console.WriteLine(max_alternate(nums2));
Console.WriteLine(max_alternate(nums3));
Console.WriteLine(max_alternate(nums4));
Console.WriteLine(max_alternate(nums5));
Console.WriteLine(max_alternate(nums6));
Console.ReadLine();
}
I'm not from a pc with a compiler right now, so I just give a try:
int max = 0;
int aux =0;
for(int i = 2 ; i < length; ++i)
{
if (!((numbers[i - 2] > numbers[i - 1] && numbers[i - 1] < numbers[i]) ||
numbers[i - 2] < numbers[i - 1] && numbers[i - 1] > numbers[i]))
{
aux = i - 2;
}
max = Math.Max(i - aux,max);
}
if (max > 0 && aux >0)
++max;
Note: should works for sequence of at least 3 elements.
There are probably a lot of ways to approach this, but here is one option:
var numbers = new int[] { 7,1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1 };
int maxCount = 0;
for (int j = 0; j+1 < numbers.Length; j++)
{
int count = 0;
if (numbers[j] < numbers[j+1])
{
count += 2;
for (int i = j+2; i+1 < numbers.Length; i += 2)
{
if (numbers[i] < numbers[i + 1] )
{
count += 2;
}
else
{
break;
}
}
}
if (maxCount < count)
{
maxCount = count;
}
}
Console.WriteLine(maxCount);
Console.ReadLine();
This solution assumes that you want a sequence of the same two alternating numbers. If that's not a requirement you could alter the second if.
Now that it's written out, it looks more complex than I had imagined in my head... Maybe someone else can come up with a better solution.
Assumes at least 2 elements.
int max = 1;
bool expectGreaterThanNext = (numbers[0] > numbers[1]);
int count = 1;
for (var i = 0; i < numbers.Length - 1; i++)
{
if (numbers[i] == numbers[i + 1] || expectGreaterThanNext && numbers[i] < numbers[i + 1])
{
count = 1;
expectGreaterThanNext = (i != numbers.Length - 1) && !(numbers[i] > numbers[i+1]);
continue;
}
count++;
expectGreaterThanNext = !expectGreaterThanNext;
max = Math.Max(count, max);
}
This works for any integers, it tracks low-hi-low and hi-low-hi just like you asked.
int numbers[] = new int[] { 1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,2,1,2,1 };
int count = 0;
int updownup = 0;
int downupdown = 0;
for(int x = 0;x<=numbers.Length;x++)
{
if(x<numbers.Length - 2)
{
if(numbers[x]<numbers[x+1])
{
if(numbers[x+1]>numbers[x+2])
{
downupdown++;
}
}
}
}
count = 0;
for(x=0;x<=numbers.Length;x++)
{
if(x<numbers.Length - 2)
{
if(numbers[x]>numbers[x+1]
{
if(numbers[x+1]<numbers[x+2])
{
updownup++;
}
}
}
}

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