Reverse SHA-256 sigma0 function within complexity of O(n)? - algorithm

Introduction
As a part of SHA-256 hashing algorithm, there's a function that is often being referred as σ1, or sigma0 for convenience. Basically, it takes X as input, where X is 32-bit unsigned value. Then converts it like this:
ROTATE_RIGHT(X, 7) ^ ROTATE_RIGHT(X, 18) ^ SHIFT_RIGHT(X, 3)
A bit of explanation, if you need one:
ROTATE_RIGHT(X, Y) - rotates X's bits to the right by Y
SHIFT_RIGHT(X, Y) - shifts X's bits to the right by Y, so the first Y bits of the result are always 0
Also, if you need the code, here's the full version in Python:
def rotate_right(x, y):
return (((x & 0xffffffff) >> (y & 31)) | (x << (32 - (y & 31)))) & 0xffffffff
def shift_right(x, n):
return (x & 0xffffffff) >> n
def sigma0(x):
return rotate_right(x, 7) ^ rotate_right(x, 18) ^ shift_right(x, 3)
Reverse function
I started wondering if that thing is reversible, and, to my surprise, it didn't take long to write a function which, by given sigma0's output, returns input of that function, or, simply put, reverses sigma0 function. I won't put the code here, because it was written in Node.js and modified a lot by more complex needs of searching particular sigma0 inputs by masks, but I'd like to give you a basic idea of how I solved it, so maybe you could enlighten me with some fresh ideas on how to achieve what I need.
My solution is simple, but it is also recursive. We know that every output's bit is the result of XOR operation of two or three input's bits. So I made a dependence table so that I can see how are output's bits are being affected by input ones:
I: 00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
R7 25,26,27,28,29,30,31,00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24
R18 14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,00,01,02,03,04,05,06,07,08,09,10,11,12,13
S3 zz,zz,zz,00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28
---------------------------------------------------------------------------------------------------
O: 00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
What is this thing about? Say, in the output's 1st bit we have 1. For convenience, I'll write it as O[0], O[1], ... O[31], so O[x] is (x+1)th bit of the output. The same for input, marked as I.
So, O[0] == 1. In the table above we see that O[0] is the result of XOR operation of I[25] and I[14]. Which implies that one and only one of those input's bits must be 1. So at this point we could say that we can create two suitable masks for the input:
##############0#########1#######
##############1#########0#######
Those masks are key to the solution, at least, to mine. # means any value (0 or 1). When we create masks, we call the recursive function for the next bit, but preserving the mask. If we are out of possible masks that would suit previous mask, the previous mask has no solution, and if we reach 32nd bit, we're guaranteed to have no sharps in the masks, and this will be the answer.
First, I need to tell you that this thing works. But on Node.js it calculates every value for about 100ms and I have no idea what is the worst complexity of my recursion algorithm, because it's quite hard to measure. It doesn't satisfy me, and I broke my brains trying to solve this O(n).
Problem
I'm wondering if it's possible to write a function that reverses sigma0 within complexity of O(n), where n is amount of bits in the input/output and it equals to 32, without recursion, masks or trees, simply and fast.
I haven't concluded any mathematical proof for my statement, but I tested lots of different values and I can confidently claim that the amount of input values is equal to the amount of output values of this function, and both are equal to 2^32 - 1. In other words, for every output, there is one and only one possible input of sigma0 function.
Which gives me a thought that the fact sigma0 original function produces result with complexity of O(n) implies that the reverse function must have a solution that also works O(n).
If you mathematically prove me that this is impossible, I'd also accept this answer, but I haven't found anything that would indicate the impossibility of this task.
Resource-devouring workaround
In case I had free 16gb of ram, I'd be able to pre-calculate all the possible values into the file, and then load it into ram as a huge array. But it's not a solution since there are other 3 similar functions, and to do that for all of them I'd need 64gb of ram which is too expensive and excessive for this simple task.
UPD: Gaussian Elimination
Thanks to Artjom B.'s comment, I found a great way to solve XOR equations via Gaussian Elimination. Currently I'm trying to solve a matrix like this:
Input: 00000000100110101000111011101001
Output: 01110001101010000010010011100110
0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 | 0
1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 | 1
2: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 | 1
3: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 | 1
4: 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 | 0
5: 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 | 0
6: 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 | 0
7: 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 | 1
8: 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 | 1
9: 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 | 0
10: 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 | 1
11: 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 | 0
12: 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 | 1
13: 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 | 0
14: 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 | 0
15: 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 | 0
16: 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 | 0
17: 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 | 0
18: 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1
19: 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 0
20: 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 0
21: 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1
22: 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 | 0
23: 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 | 0
24: 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 | 1
25: 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 | 1
26: 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 | 1
27: 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 | 0
28: 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 | 0
29: 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 | 1
30: 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 | 1
31: 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 | 0
Published the matrix so you could see how it looks and not waste your time on creating it by yourself. I'll update my question once I solved it or not.

If we view sigma0 as a function over a GF(2)32 vector, you will note that it's linear. Addition in GF(2)32 is just the binary XOR:
>>> sigma0(235 ^ 352124)
2045075788
>>> sigma0(235) ^ sigma0(352124)
2045075788
This means that if we can find sigma0(x0) = 0b1, sigma0(x1) = 0b10, etc we can easily invert anything bit-by-bit. We can easily find these inverses with z3:
import z3
def z3_sigma0(x):
return z3.RotateRight(x, 7) ^ z3.RotateRight(x, 18) ^ z3.LShR(x, 3)
s = z3.Solver()
xs = [z3.BitVec(f"x{i}", 32) for i in range(32)]
for i in range(32):
s.add(z3_sigma0(xs[i]) == (1 << i))
print(s.check())
m = s.model()
for i in range(32):
print("x{:02} = 0x{:08x}".format(i, m[xs[i]].as_long()))
This instantly outputs:
sat
x00 = 0x185744e9
x01 = 0x30ae89d2
x02 = 0x615d13a4
x03 = 0xdaed63a1
x04 = 0x9cd03a8e
x05 = 0x08fdcc39
x06 = 0x11fb9872
x07 = 0x23f730e4
x08 = 0x5fb92521
x09 = 0xbf724a42
x10 = 0x57ee6948
x11 = 0xafdcd290
x12 = 0x76b358ec
x13 = 0xf531f531
x14 = 0xc36917ae
x15 = 0xb78f9679
x16 = 0x4615d13e
x17 = 0x947ce695
x18 = 0x19a4740f
x19 = 0x2b1facf7
x20 = 0x4e681d07
x21 = 0x84877ee7
x22 = 0x385344eb
x23 = 0x70a689d6
x24 = 0xf91a5745
x25 = 0xc36917af
x26 = 0xb78f967b
x27 = 0x4615d13a
x28 = 0x8c2ba274
x29 = 0x290afdcd
x30 = 0x4a42bf73
x31 = 0x94857ee6
Thus we can use this to make our inversion function:
sigma0_singleton_inverses = [
0x185744e9, 0x30ae89d2, 0x615d13a4, 0xdaed63a1, 0x9cd03a8e, 0x08fdcc39,
0x11fb9872, 0x23f730e4, 0x5fb92521, 0xbf724a42, 0x57ee6948, 0xafdcd290,
0x76b358ec, 0xf531f531, 0xc36917ae, 0xb78f9679, 0x4615d13e, 0x947ce695,
0x19a4740f, 0x2b1facf7, 0x4e681d07, 0x84877ee7, 0x385344eb, 0x70a689d6,
0xf91a5745, 0xc36917af, 0xb78f967b, 0x4615d13a, 0x8c2ba274, 0x290afdcd,
0x4a42bf73, 0x94857ee6
]
def inv_sigma0(x):
r = 0
for i in range(32):
if x & (1 << i):
r ^= sigma0_singleton_inverses[i]
return r
And indeed:
>>> def test_inv_once():
... r = random.randrange(2**32)
... return inv_sigma0(sigma0(r)) == r
>>> all(test_inv_once() for _ in range(10**6))
True
The above can be written completely loopless and branchless:
def inv_sigma0(x):
xn = ~x
r = (((xn >> 0) & 1) - 1) & 0x185744e9
r ^= (((xn >> 1) & 1) - 1) & 0x30ae89d2
r ^= (((xn >> 2) & 1) - 1) & 0x615d13a4
r ^= (((xn >> 3) & 1) - 1) & 0xdaed63a1
r ^= (((xn >> 4) & 1) - 1) & 0x9cd03a8e
r ^= (((xn >> 5) & 1) - 1) & 0x08fdcc39
r ^= (((xn >> 6) & 1) - 1) & 0x11fb9872
r ^= (((xn >> 7) & 1) - 1) & 0x23f730e4
r ^= (((xn >> 8) & 1) - 1) & 0x5fb92521
r ^= (((xn >> 9) & 1) - 1) & 0xbf724a42
r ^= (((xn >> 10) & 1) - 1) & 0x57ee6948
r ^= (((xn >> 11) & 1) - 1) & 0xafdcd290
r ^= (((xn >> 12) & 1) - 1) & 0x76b358ec
r ^= (((xn >> 13) & 1) - 1) & 0xf531f531
r ^= (((xn >> 14) & 1) - 1) & 0xc36917ae
r ^= (((xn >> 15) & 1) - 1) & 0xb78f9679
r ^= (((xn >> 16) & 1) - 1) & 0x4615d13e
r ^= (((xn >> 17) & 1) - 1) & 0x947ce695
r ^= (((xn >> 18) & 1) - 1) & 0x19a4740f
r ^= (((xn >> 19) & 1) - 1) & 0x2b1facf7
r ^= (((xn >> 20) & 1) - 1) & 0x4e681d07
r ^= (((xn >> 21) & 1) - 1) & 0x84877ee7
r ^= (((xn >> 22) & 1) - 1) & 0x385344eb
r ^= (((xn >> 23) & 1) - 1) & 0x70a689d6
r ^= (((xn >> 24) & 1) - 1) & 0xf91a5745
r ^= (((xn >> 25) & 1) - 1) & 0xc36917af
r ^= (((xn >> 26) & 1) - 1) & 0xb78f967b
r ^= (((xn >> 27) & 1) - 1) & 0x4615d13a
r ^= (((xn >> 28) & 1) - 1) & 0x8c2ba274
r ^= (((xn >> 29) & 1) - 1) & 0x290afdcd
r ^= (((xn >> 30) & 1) - 1) & 0x4a42bf73
r ^= (((xn >> 31) & 1) - 1) & 0x94857ee6
return r
The fastest version probably probably is this one, grouping by 16 bits at a time using a 2 × 216 size lookup table (or similarly four lookups into a 4 × 28 sized table).
sigma0_16bit_inverse_lo = [inv_sigma0(x) for x in range(2**16)]
sigma0_16bit_inverse_hi = [inv_sigma0(x << 16) for x in range(2**16)]
def fast_inv_sigma0(x):
return (sigma0_16bit_inverse_lo[x & 0xffff] ^
sigma0_16bit_inverse_hi[(x >> 16) & 0xffff])

Related

Find 4-neighbors using J

I'm trying to find the 4-neighbors of all 1's in a matrix of 0's and 1's using the J programming language. I have a method worked out, but am trying to find a method that is more compact.
To illustrate, let's say I have the matrix M—
] M=. 4 4$0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0
0 0 1 0
0 0 0 0
0 0 0 0
and I want to generate—
0 0 1 0
0 1 0 1
0 0 1 0
0 0 0 0
I've sorted something close (which I owe to this little gem: https://www.reddit.com/r/cellular_automata/comments/9kw21u/i_made_a_34byte_implementation_of_conways_game_of/)—
] +/+/(|:i:1*(2 2)$1 0 0 1)&|.M
0 0 1 0
0 1 2 1
0 0 1 0
0 0 0 0
which is fine because I'll be weighting the initial 1's anyway (and the actual numbers aren't really that important for my application anyway). But I feel like this could be more compact and I've just hit a wall. And the compactness of the expression actually is important to my application.
Building on #Eelvex comment solution, if you are willing to make the verb dyadic it becomes pretty simple. The left argument can be the rotation matrix and then the result is composed with +./ which is a logical or and can be weighted however you want.
] M0=. 4 4$0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0
0 0 1 0
0 0 0 0
0 0 0 0
] m =.2,\5$0,i:1
0 _1
_1 0
0 1
1 0
m +./#:|. M0
0 0 1 0
0 1 0 1
0 0 1 0
0 0 0 0
There is still an issue with the edges (which wrap) around, but that also occurs with your original solution, so I am hoping that you are not concerned with that.
] M1=. 4 4$1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
m +./#:|. M1
0 1 0 1
1 0 0 0
0 0 0 0
1 0 0 0
If you did want to clean that up, you can use the slightly longer m +./#:(|.!.0), which fills the rotation with 0's.
] M2=. 4 4$ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 1
m +./#:(|.!.0) M2
0 0 0 0
0 0 0 0
0 0 0 1
0 0 1 0
m +./#:(|.!.0) M1
0 1 0 0
1 0 0 0
0 0 0 0
0 0 0 0

problem with extract region pixels from gray image on Matlab

I would to like to selection the dark gray pixels from gray image.
J = rgb2gray(I);
Newfigure = zeros(size(J));
[k,l] =find(J<130);
Newfigure(k,l) = J(k,l);
imshow(Newfigure)
when visualize the Newfigure, I see the zone of circle like square. Why does this happen?
This is due to the way you index into Newfigure. Look at the following:
>> test = zeros(10);
>> test([2,8], [1,2]) = 1
test =
0 0 0 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
This is different from
>> test = zeros(10);
>> test(2, 1) = 1;
>> test(8, 2) = 1
test =
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
You could either use a loop like
Newfigure = zeros(size(J));
for n = 1:numel(k);
Newfigure(k(n), l(n)) = J(k(n), l(n));
end
or simply use
Newfigure = J < 130;
imshow(Newfigure);
Get rid of the find(...) and just use logical indices. It'll be faster too...
J = rgb2gray(I);
Newfigure = zeros(size(J));
tf = J<130;
Newfigure(tf) = J(tf);
imshow(Newfigure)
The tf variable will be an array of 0s/1s (true/false), the same size as J which you can then use to index the arrays as shown.

Computing block sum for an arbitrary region in an image

I wonder what is the most effective way to solve the following problem:
(If there is a name for this problem, I would like to know it as well)
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0 1 0 1 1 1;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1 1 1 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
If I have an image where I am interested in the following pixels marked by 1. In an image I want to calculate a sum around this block. A sum of block is easy to calculate from an integral image but I don't want to do it for the whole image, since there is a lot of unnecessary computation.
One option that I can come up with is to search the minimum and maximum in horizontal and vertical directions and then take a rectangular portion of the image enlarged so that it will covered the block portion. For example +2 pixels each directions, if the block size is 5. But this solution still includes unnecessary calculation.
If I had a list of these indices, I could loop through them and calculate the sum for each block but then if there is another pixel close by which has the same pixels in its block, I need to recalculate them and If I save them, I somehow need to look if they are already calculated or not and that takes time as well.
Is there a known solution for this sort of a problem?

Algorithm to divide black and white chocolate bar with least number of breaks

I have a rectangular chocolate bar that consists of squares either black, white or mixed. The bar being not bigger than 50x50 squares. I'm supposed to divide the bar between two people(one gets all the white squares and one the black ones, mixed ones don't matter), by cracking it either horizontally or vertically. I'm supposed to find a method with the least amount of such cracks.
I'm given this input:
M N (number of rows, number of columns)
and then M rows that are N numbers long(0 means white, 1 means black, 2 is mixed)
so for example bar described like this:
4 4
0 1 1 1
1 0 1 0
1 0 1 0
2 0 0 0
can be divided by cracking it seven times in total.
And this one needs at least 24 cracks:
5 8
0 1 0 1 0 1 0 2
1 0 2 0 2 0 1 0
0 2 0 2 0 1 0 2
1 0 2 0 2 0 1 0
0 1 0 1 0 1 0 2
I was thinking about something like cracking the bar in two pieces so that the sum of sums of future cracks needed to divide two newly made chocolate bar pieces is the least possible from all the possible cracks (which is height -1 + width -1 of the current bar piece being cracked)
I managed, also thanks to zenwraight, to write a code that solves this but I encountered another problem, it is really inefficient and if the starting chocolate bar gets bigger than 30x30 it's practically unusable.
Anyway the source code (written in C):
#include <stdio.h>
#include <stdlib.h>
const int M, N;
int ****pieces;
int r = 0;
int ri = 0;
int inf;
void printmatrix(int **mat, int starti, int startj, int maxi, int maxj) {
for (int i = starti; i < maxi; i++) {
for (int j = startj; j < maxj; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
}
int minbreaks(int **mat, int starti, int startj, int maxi, int maxj, int depth) {
if (pieces[starti][startj][maxi][maxj] != 0) {
r++;
return pieces[starti][startj][maxi][maxj];
} else {
ri++;
int vbreaks[maxj - 1];
int hbreaks[maxi - 1];
for (int i = 0; i < maxj; i++) {
vbreaks[i] = inf;
}
for (int i = 0; i < maxi; i++) {
hbreaks[i] = inf;
}
int currentmin = inf;
for (int i = starti; i < maxi; i++) {
for (int j = startj; j < maxj - 1; j++) {//traverse trough whole matrix
if (mat[i][j] != 2) {
for (int k = startj + 1; k < maxj; k++) {//traverse all columns
if (vbreaks[k - 1] == inf) {//traverse whole column
for (int z = starti; z < maxi; z++) {
if (mat[z][k] != 2 && mat[i][j] != mat[z][k]) {
/* printmatrix(mat, starti, startj, maxi, maxj);
printf("brokenv in depth:%d->\n", depth);
printmatrix(mat, starti, startj, maxi, k);
printf("and\n");
printmatrix(mat, starti, k, maxi, maxj);
printf("****\n");*/
vbreaks[k - 1] = minbreaks(mat, starti, startj, maxi, k, depth + 1) + minbreaks(mat, starti, k, maxi, maxj, depth + 1);
if (vbreaks[k - 1] < currentmin) {
currentmin = vbreaks[k - 1];
}
break;
}
}
}
}
}
}
}
for (int i = starti; i < maxi - 1; i++) {
for (int j = startj; j < maxj; j++) {
if (mat[i][j] != 2) {
for (int k = starti + 1; k < maxi; k++) {
if (hbreaks[k - 1] == inf) {
for (int z = startj; z < maxj; z++) {
if (mat[k][z] != 2 && mat[i][j] != mat[k][z]) {
/* printmatrix(mat, starti, startj, maxi, maxj);
printf("brokenh in depth:%d->\n", depth);
printmatrix(mat, starti, startj, k, maxj);
printf("and\n");
printmatrix(mat, k, startj, maxi, maxj);
printf("****\n");*/
hbreaks[k - 1] = minbreaks(mat, starti, startj, k, maxj, depth + 1) + minbreaks(mat, k, startj, maxi, maxj, depth + 1);
if (hbreaks[k - 1] < currentmin) {
currentmin = hbreaks[k - 1];
}
break;
}
}
}
}
}
}
}
if (currentmin == inf) {
currentmin = 1;
}
pieces[starti][startj][maxi][maxj] = currentmin;
return currentmin;
}
}
void alloc(int i, int j) {
pieces[i][j] = malloc(sizeof (int*)*(M + 1));
for (int y = i; y < M + 1; y++) {
pieces[i][j][y] = malloc(sizeof (int)*(N + 1));
for (int x = j; x < N + 1; x++) {
pieces[i][j][y][x] = 0;
}
}
}
int main(void) {
FILE *file = fopen("pub08.in", "r");
//FILE *file = stdin;
fscanf(file, "%d %d", &M, &N);
int **mat = malloc(sizeof (int*)*M);
pieces = malloc(sizeof (int***)*M);
for (int i = 0; i < M; i++) {
mat[i] = malloc(sizeof (int)*N);
pieces[i] = malloc(sizeof (int**)*N);
for (int j = 0; j < N; j++) {
int x;
fscanf(file, "%d", &x);
mat[i][j] = x;
alloc(i, j);
}
}
inf = M * (M + 1) * N * (N + 1) / 4 + 1;
int result = minbreaks(mat, 0, 0, M, N, 0);
printf("%d\n", result);
printf("ri:%d,r:%d\n", ri, r);
return (EXIT_SUCCESS);
}
I am aiming to solve this input :
40 40
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 1 2 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 0 2 1 2 1 2 0 0 1 2 2 0 0 0 0 0 0 0 0 1 1 2 1 2 0 0 0 0 0 0 0 0 0 0
0 0 0 1 2 2 0 1 1 1 1 1 0 0 1 2 2 0 0 0 0 0 1 0 0 2 2 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 2 2 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1 1 2 2 0 0 0 1 2 2 1 2 1 0 0 0 0 0 1 2 1 2 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 2 2 1 2 0 0 0 0 0 2 1 2 2 0 0 0 0 0 2 1 2 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 2 2 2 1 1 0 0 0 0 0 2 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 0 0
0 2 1 2 1 0 2 2 2 2 1 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 1 2 0 2 2 1 0 0 0 0 0 0
0 2 2 1 2 0 1 2 2 1 1 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0
0 2 2 1 2 0 0 0 0 2 1 2 1 2 1 1 2 0 2 0 0 0 0 0 0 0 1 2 2 2 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 2 2 2 2 1 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 1 2 1 1 2 2 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 2 2 0 0 0 0
0 0 0 0 0 0 0 2 1 2 0 0 2 2 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 1 1 0 0 0 0
0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 2 0 0 0 0
0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 2 2 1 0 0 0 0 2 0 1 1 1 2 1 2 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 2 0 0 0 0 0 0 2 1 2 2 2 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 2 0 0 0 0 0 0 1 2 1 1 2 2 0 0 0 0 0
0 0 0 0 0 0 1 2 1 2 2 1 0 0 0 0 0 0 0 1 2 1 2 0 0 0 0 0 0 0 0 0 2 1 2 0 0 0 0 0
0 0 0 0 0 0 1 2 2 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 2 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0
0 0 0 0 0 0 1 1 1 1 1 2 2 0 0 0 0 0 0 0 0 1 1 1 2 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0
0 0 0 0 0 0 1 2 2 2 1 1 1 0 0 0 0 0 0 0 0 1 2 1 2 0 0 0 0 0 0 0 0 0 0 2 2 2 1 0
0 0 0 0 0 0 0 0 0 1 2 1 2 0 0 0 0 0 0 0 0 1 1 1 2 2 0 0 0 0 0 0 0 0 0 1 2 1 1 0
0 0 0 2 1 1 2 2 0 1 2 1 1 0 0 0 0 0 2 2 1 2 2 1 2 2 0 0 0 0 0 0 0 0 0 1 2 2 2 0
0 0 0 2 2 2 1 1 0 0 1 2 2 2 0 0 0 0 2 2 2 1 1 2 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 2 1 2 2 1 1 0 2 1 2 1 2 1 2 1 1 2 1 1 1 1 1 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 2 2 2 2 1 0 1 1 1 1 1 1 2 1 1 2 2 1 0 1 2 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 0 0 2 1 1 1 2 1 2 0 0 1 2 1 2 1 2 2 0 0 0 0 0 0 0 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 2 2 1 1 2 2 1 1 1 1 1 1 1 2 1 0 0 0 0 0 0 0 2 2 2 0 0 0
0 0 0 0 0 0 0 1 1 1 2 0 0 1 1 1 2 2 1 2 2 2 1 0 0 0 1 1 1 0 0 0 0 0 1 2 1 0 0 0
0 0 0 0 0 0 0 2 1 1 2 0 0 0 0 0 0 2 2 2 1 1 1 0 0 0 1 2 2 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 0 0 2 1 1 1 2 0 0 0 0 1 2 2 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 2 2 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 0 0 0 1 1 2 0 2
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 0 0 0 1 2 1 0 0
0 0 0 0 0 0 0 0 0 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 0 0 0 0 0 0 0 1 2 1 0 0
0 0 0 0 0 0 0 0 0 2 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
in under 2 seconds, which is much much faster time than that of my current program. The minimum amount of cracks for this one is 126.
Nice question, I have an approach in mind which makes use of recursion to tackle the above problem.
So as at each level or step you have two options either to split the bar horizontally or vertically.
So let's understand the algorithm with an example.
Example:-
4 4
0 1 1 1
1 0 1 0
1 0 1 0
2 0 0 0
Now let's call our function minBreaks(int n, int m, int matleft, int right)
So at first step if we break is horizontally our matleft will be
0
1
1
2
and matright will be
1 1 1
0 1 0
0 1 0
0 0 0
Now similarly if we had broken this vertically our matleft will be
0 1 1 1
and matright will be
1 0 1 0
1 0 1 0
2 0 0 0
Now you pass along this matleft and matright in next recursion call
And then at each call when size of row = 1 or col = 1, you can check the connected components of same value and return the count of connected components
Like for example for vertically case of maxleft -> 0 1 1 1, you will return 2.
Similarly for all the cases and the end part of the method will return
min between break horizontally and vertically
Hope this helps!

Grundy Number For a Matrix [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
How to calculate the Grundy number for states of a 4*4 matrix. A valid move consists of transforming the 1s into 0s of a submatrix having all 1s.
Example:
1010
0011
0000
0000
Grundy Number = 2
I checked for smaller cases and calculated the Grundy number for that, but couldn't extend it for any binary 4*4 matrix. So, please help me to calculate this.
Note: Can convert 1 to 0 only in submatrix.
The Grundy number is calculated recursively through the reachable positions:
Start with the final position (all zeros) which is a loss (0).
0 0 0 0
0 0 0 0 = 0
0 0 0 0
0 0 0 0
Proceed to add ones to the matrix to get the values for the other configurations. Some examples with exactly one 1.
1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 = 0 0 0 0 = 0 0 1 0 = 0 0 0 1 = 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
For two 1s we have to distinguish if the 1s are adjacent and can be removed in one move or not.
1 0 1 0 1 0 0 0 1 0 0 0* 0 0 1 0
0 0 0 0 = 0 0 1 0 = 0 0 0 1 = 0 0 0 1 = 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 1 0 = 0 0 1 1 = 2
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
The same for three and more 1s.
1 0 1 0*
0 0 0 1 = 1
0 0 0 0
0 0 0 0
1 0 1 0* 1 0 0 0* 0 0 1 0*
0 0 1 0 = 0 0 1 1 = 0 0 1 1 = 3
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Finally we can evaluate the given matrix. Reachable positions from the example are marked with a star *. So we can easily see that the number we are looking for is mex(0, 1, 3) = 2.
1 0 1 0
0 0 1 1 = 2
0 0 0 0
0 0 0 0
A pseudo program could look as simple as this (the grundy function has to support scalar state and arrays or vectors of states for this to work):
grundy(0) = 0
grundy(state) = mex(grundy(reachableStates(state)))

Resources