Need help changing a method from recursive to iterative - java

I'm working on a program that will fix data corruption given the data and the hash of the correct data. It starts to get slow after about 4 or 5 bits are corrupted even when only dealing with only a few bytes of data, so I thought I would make it iterative instead of recursive. After doing some research, I found that I could use a stack to do so. I'm currently having trouble with finding the correct place to pop the variables off of the stack. Here's the original method.
private static void fixFile(byte[] data, byte[] hash, byte[] correctHash, MessageDigest hasher, long depth)
{
int len = data.length;
outer: for(int i = 0; i < len; i++)
{
byte origVal = data[i];
for(int j = 0; j < 8; j++)
{
data[i] = (byte) (data[i] ^ (1 << j));
if(depth > 1)
fixFile(data, hash, correctHash, hasher, depth - 1);
hash = hasher.digest(data);
if(!Arrays.equals(correctHash, hash))
data[i] = origVal;
else
break outer;
}
}
}
Here's the modified method where I tried to make it iterative.
private static void fixFile(byte[] data, byte[] hash, byte[] correctHash, MessageDigest hasher, long depth)
{
Stack stack = new Stack<Integer>();
int len = data.length;
outer: for(int i = 0; i < len; i++)
{
byte origVal = data[i];
for(int j = 0; j < 8; j++)
{
data[i] = (byte) (data[i] ^ (1 << j));
if(depth > 1)
{
stack.push(depth);
stack.push(i);
stack.push(j);
depth--;
i = -1;
j = 0;
continue outer;
}
else
{
// where do I put this to make it work.
j = stack.pop();
i = stack.pop();
depth = stack.pop();
}
hash = hasher.digest(data);
if(!Arrays.equals(correctHash, hash))
data[i] = origVal;
else
break outer;
}
}
}

I think your recursive approach is ideal for this problem and all the time will be being spent in the digest function.
However, when you are fixing depth bits you can get a factorial(depth) improvement in the speed of the program. (So for 6 bits this will make the program go 6*5*4*3*2*1 = 720 times faster.)
The issue is that your current code currently toggles depth bits in your data, but in any order. i.e. when it toggles three bits it will try toggling bits 1,2,3 and 2,1,3 and 3,1,2 and 3,2,1 and 1,3,2 and 2,3,1 (amongst many other choices). Note that in each of these cases exactly the same 3 bits are toggled so there is no point testing them all.
You can fix this by passing additional parameters to your code saying where the last bit was toggled (i.e. pass in i and j as i_base and j_base), and then only allow your code to toggle new bits if they are at a later position in the data (i.e. if i>i_base || (i==i_base && j>j_base)).

Related

Find the first duplicate with the minimal next occurrence

I am trying to solve a challenge,
I wrote my solution and it passes all test cases except some hidden test cases. I can't think another case in which my method fails and don't know what to do anymore.
Here it is:
int firstDuplicate(int[] a) {
int[] indexCount;
int duplicate, temp;
boolean check;
duplicate = -1; temp = a.length;
indexCount = new int[a.length];
check = false;
for( int i = 0; i < a.length; i++ ){
if( indexCount[a[i]-1] == 0 ){
indexCount[a[i]-1] = i+1;
check = false;
}else{
indexCount[a[i]-1] = (i+1) - indexCount[a[i]-1];
check = true;
}
if( check && indexCount[a[i]-1] < temp ){
duplicate = a[i];
temp = indexCount[a[i]-1];
}
}
return duplicate;
}
Instructions are:
Write a solution with O(n) time complexity and O(1) additional space complexity.
Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index.
Example
For a = [2, 3, 3, 1, 5, 2], the output should be
firstDuplicate(a) = 3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3 has a smaller index than than second occurrence of 2 does, so the answer is 3.
For a = [2, 4, 3, 5, 1], the output should be
firstDuplicate(a) = -1.
Here is what I have. Runs in O(n) and uses O(1) space. Correct me if I'm wrong here.
Since my input cannot have a value that's more than the length, I can use mod operator for indexing on the same array and add the length to the value in index. As soon as I encounter a value that larger than the length, that means I've already incremented that before, which gives me the duplicate value.
public int firstDuplicate(int[] arr) {
int length = arr.length;
for (int i = 0; i < length; i++) {
int expectedIndex = arr[i] % length;
if (arr[expectedIndex] > length) {
return arr[i] > length ? arr[i] - length : arr[i];
} else {
arr[expectedIndex] += length;
}
}
return -1;
}
This answer is based on #Mehmet-Y's answer and all credit goes to Mehmet-Y. This version addresses the three issues I pointed out in the comments. I will delete this answer if the original gets corrected.
The general approach is to use the original array for storage instead of allocating a new one. The fact that no value may be less than one or greater than the length suggests that you can use the array as a set of indices to flag an element as "already seen" by either negating it or adding/subtracting the array length to/from it.
To achieve O(n) time complexity, you have to solve the problem in a fixed number of passes (not necessarily one pass: the number just can't depend on the size of the array).
But how do you decide which duplicate has the smallest second index? I would suggest using two different flags to indicate an index that is already seen vs. the second item in a duplicate pair. For this example, we can set the index flag by incrementing the elements by the length, and marking duplicates by negating them. You will need a second pass to find the first negagive in the array. You can also use that pass to restore the elements to their original values without sacrificing O(n) time complexity.
Here is a sample implementation:
int firstDuplicate(int[] a)
{
// assume all elements of a are in range [1, a.length]
// An assertion of that would not increase the time complexity from O(n)
int len = a.length;
for(int i = 0; i < len; i++) {
// a[i] may be > len, but not negative.
// Index of bin to check if this element is already seen.
flagIndex = (a[i] - 1) % len;
if(a[flagIndex] > len) {
// If already seen, current element is the second of the pair.
// It doesn't matter if we flag the third duplicate,
// just as long as we don't tag the first be accident.
a[i] = -a[i];
} else {
// Flag the element as "already seen".
// This can be done outside the else, but you might run
// into (more) overflow problems with large arrays.
a[flagIndex] += len;
}
}
// Search and stash index of first negative number
for(int i = 0; i < len; i++) {
if(a[i] < 0) {
return -a[i] % len;
}
}
// Nothing found, oh well
return -1;
}
If you want to take advantage of the second pass to restore the original values of the array, replace
for(int i = 0; i < len; i++) {
if(a[i] < 0) {
return -a[i] % len;
}
}
return -1;
with
int duplicate = -1;
for(int i = 0; i < len; i++) {
if(a[i] < 0) {
a[i] = -a[i];
if(duplicate == -1) {
duplicate = a[i] % len;
}
}
a[i] %= len;
}
return duplicate;

Is this encryption function reversible?

I'm currently working on a small challenge, trying to figure out how an unnamed encryption algorithm works. The original algorithm looks like this:
public final String a(byte[] original)
{
this.a = original.length;
byte[] solution = new byte[8];
int i = 0;
int base = 13;
for (int si = 0; si < 8; si++)
{
for (int oi = 0; oi < a; oi++)
{
byte current = original[oi];
solution[i] = ((byte)(solution[i] + (current ^ base)));
base = (base ^ i) + solution[i];
i = (i + 1) % 8;
}
}
char[] result = new char[8];
for (int n = 0; n < 8; n++) {
result[n] = ((char)((solution[n] & 0x3F) + 48));
}
return String.valueOf(result);
}
So every string that gets passed to this function as a byte[] array will be encoded into a 8-char somewhat cryptic text. I've found out other things about this:
The encoded characters in the char[] result always have literals with values between 48 and 111 (0x3F + 48).
When decoding, the first step would be subtracting 48 and then undo the & operation. Since 0x3F equals the binary representation 111111, the value of the original byte is one of 4 possibilities:
00xxxxxx: the missing 2 bits were both zero.
01xxxxxx: the lower addressed bit of both was one.
10xxxxxx: the higher addressed bit of both was one.
11xxxxxx: both of them were one.
Meaning, it could be one of four characters. I initially thought about reversing the algorithm, but I'm asking you if this is even possible for this kind of algorithm. I tried it and came this far:
public static String b(String encrypted) {
byte[][] matrix = new byte[4][20];
byte[] word = encrypted.getBytes();
for(int i = 0; i < 4; i++) {
for(int j = 0; j < word.length; j++) {
byte tmp = (byte)(word[i] - 48);
matrix[i][j] = (byte)(tmp + i);
}
}
}
I currently subtract 48 and insert all 4 possibilities into a 2D-array. But im stuck solving the nested for loop, especially the variables i and base are hard to find out. The only information I have is the encrypted word and the fact that the original word was 20 literals long at MAX (Hence the [4][20] dimensions).
The encryption doesn't look familiar to me, which leaves me no options to look for the name of this algorithm.
If it is possible to reverse this algorithm, what would my next step be?
No, that obviously can't be reversible in the general case.
There are effectively 40 bits of information in the output (eight bytes, at 5 bits each -- & 0x1F limits each one to five bits). This means that there are only 240 possible outputs; there are far more possible inputs than that.
If there are some constraints on the input -- for instance, if its length is known to be short -- it might be possible to make some inferences about that. However, you haven't stated any constraints, so…

Can't Convert an Int Array to ASCII

to the point.
I don't know what make my function error but here when i want to convert an int Array to ASCII character, i got some errors which says
java.lang.OutOfMemoryError: Failed to allocate a 51529974 byte allocation with 4194304 free bytes and 30MB
I think my function doesn't right enough to convert it.
Here is my function :
public static String[] DectoASCII(int[] resultXORDec,int jumKat) {
int length = jumKat;
String ASCIIfromDec[] = new String[jumKat];
for(int i=0;i<jumKat;i++) {
StringBuilder builder = new StringBuilder(length);
for (int j = length - 1; j >= 0; i--) {
builder.append((char) ((resultXORDec[j] >> (8 * i)) & 0xFF));
}
ASCIIfromDec[i]=builder.toString();
Log.d("ascifrom",ASCIIfromDec[i]);
}
return ASCIIfromDec;
}
}
Please master, help me. Is there any other way to convert int (Decimal) to ASCII code?
Thanks..
Well, I guess the j index does not change in this loop:
for (int j = length - 1; j >= 0; i--) {
builder.append((char) ((resultXORDec[j] >> (8 * i)) & 0xFF));
}
thus you have an infinite loop, so builder gets bigger and bigger.

Java 2D array encirclement

I am tasked to make a program which returns true if in a 2D array 1-s encircle 0-s.
I tried something like this, but i cant find the right solution.
public boolean checkGameState(){
for(int i=0;i<fields.length;i++){
for(int j=0;j<fields.length;j++){
if(fields[i][j]!=0){
if(row(i,j){
return true;
}
}
}
}
return false;
}
private boolean row(int a, int b){
int checkI=a;
int checkJ=b;
while(fields[checkI][checkJ]==1){
checkJ++;
}
while(fields[checkI][checkJ]==1){
checkI++;
}
while(fields[checkI][checkJ]==1){
checkJ--;
}
while(fields[checkI][checkJ]==1){
checkI--;
}
return a==checkI && b==checkJ;
}
The 2D array looks something like this:
111100
100100
100101
111100
001100
For this array the method should return true.
The easiest way might be to use a flood fill algorithm to eliminate all the zeros that are not encircled by ones, and then checking whether there are any left.
First, put all the zeros directly on the "fringe" of the 2D array into a queue. Then, use the flood fill algorithm to turn all of those into a different number (e.g., 2), and add the nodes next to them to the fringe set (either diagonally or only direct neighbours). Repeat until there are no more nodes in the fringe. Finally, check whether there are any more zeros in the array. If so, those were not connected to the fringe region and thus had to be "encircled" by ones.
// test data set up
int[][] data = {{1,1,1,1,0,0},
{1,0,0,1,0,0},
{1,0,0,1,0,1},
{1,1,1,1,0,0},
{0,0,1,1,0,0}};
int N = data.length, M = data[0].length;
// create queue of zeros on the "fringe"
Queue<int[]> fringe = new LinkedList<>();
for (int i = 0; i < N; i++) {
if (data[i][0 ] == 0) fringe.add(new int[]{i,0 });
if (data[i][M-1] == 0) fringe.add(new int[]{i,M-1});
}
for (int j = 0; j < M; j++) {
if (data[0 ][j] == 0) fringe.add(new int[]{0 ,j});
if (data[N-1][j] == 0) fringe.add(new int[]{N-1,j});
}
// do flood fill until no more zeros reachable
while (! fringe.isEmpty()) {
int[] next = fringe.poll();
int i = next[0], j = next[1];
data[i][j] = 2;
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
try {
if (data[i+di][j+dj] == 0) fringe.add(new int[]{i+di, j+dj});
} catch (ArrayIndexOutOfBoundsException e) {}
}
}
}
// check for remaining zeros
boolean encircled = false;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
System.out.print(data[i][j]);
encircled |= data[i][j] == 0;
}
System.out.println();
}
System.out.println(encircled);
Example output:
111122
100122
100121
111122
221122
true
The complexity should be on the order of O(NxM), since each of the NxM nodes can only appear once in the queue (plus a bit of overhead for constructing the queue and finding remaining zeros).
Please note that I have assumed that you need rectangle shape surrounding
You need to find sequences for 3 or more 1 in one row.
xx1111xx // x means any number
For each sequence check if there is sequence of the same length 2 or more rows lower.
xx1111xx
xxxxxxxx
xx1111xx
For each "pair" of sequences check if they are connected with 1 on the edges.
xx1111xx
xx1xx1xx
xx1111xx

What's wrong with my bit vector?

I am trying to create a bit vector backed by an int[].
So I have the following code:
public class BitVector {
int[] vector = new int[1 << 16];
public void setBit(int nextInt) {
nextInt = nextInt & 0xFFFF;
int pos = nextInt / 32;
int offset = nextInt % 32;
vector[pos] |= (1 << offset);
}
public int findClearedBit() {
for(int i = 0; i < vector.length; i++){
for(int j = 0; j < 8; j++){
if((vector[i] & (1 << j)) == 0)
return i * 32 + j;
}
}
return -1;
}
}
I know that perhaps I should have used byte[] instead etc but I was wondering why this way it does not work.
The idea is that I pass in int from a stream and keep the lower 16 bits and mark the corresponding bit as set. So when I iterate over the vector I will find the number (indicate by the lower 16 bits) missing.
But I get wrong result. So I believe my handing is wrong.
Any ideas?
Update:
I have a stream of 32-bit integers. As I read them in I try to mark a number missing by using the lower 16-bits and setting the bitvector (code posted).
I also try to find the upper 16 bits missing reading the stream a second time.
So while the missing number is: 231719592 = (1101110011111100001010101000) = (3535-49832)
When I read the stream I don't get 49832 as the missing lower bits but 65536
Update2:
public int findMissingInt(File f)throws Exception{
Scanner sc = new Scanner(f);
int SIZE = 1 << 16;
int[] occurences = new int[SIZE];
while(sc.hasNext()){
occurences[getIdx(sc.nextInt())]++;
}
int missingUpper = -1;
for(int i = 0; i < occurences.length; i++){
if(occurences[i] < SIZE){
System.out.println("Found upper bits:"+i);
missingUpper = i;
break;
}
}
if(missingUpper == -1){
return -1;
}
//Arrays.fill(occurences, 0); //I reused this. Bellow changed after answer of Peter de Rivaz
BitVector v = new BitVector(new int[1 << (16-5)]);
sc = new Scanner(f);
while(sc.hasNext()){
v.setBit(sc.nextInt());
}
int missingLower = v.findClearedBit();
System.out.println("Lower bits:"+missingLower);
return createNumber(missingUpper, missingLower);
}
private int createNumber(int missingUpper, int missingLower) {
int result = missingUpper;
result = result << 16;
return result | missingLower;
}
public int getIdx(int nextInt) {
return (nextInt >>> 16);
}
I get:
Missing number=231719592
Found upper bits:3535 //CORRECT
Lower bits:-1 //WRONG
Actual missing number=-1 //WRONG
I think there are two problems:
Your array has 65536 entries, but you store 32 bits in each entry, so you only need 65536/32 entries in it.
You store 32 bits in each int, but only check j from 0 to 7 when finding gaps
The first bug means that your program reports 65536 as a missing 16bit number.
The second bug means that your program does not spot the missing number.
i.e. change
int[] vector = new int[1 << 16];
to
int[] vector = new int[1 << (16-5)];
and change
for(int j = 0; j < 8; j++)
to
for(int j = 0; j < 32; j++)
EDIT
Judging from the comments, the question is actually how to find a missing number with limited RAM. The answer to this question can be found here on stackoverflow.
There is an additional bug in the higher level code.
During the second pass that populates the bitset, you should only include numbers that have the matching upper bits.
i.e. change
v.setBit(sc.nextInt());
to something like
int nx = sc.nextInt();
if (getIdx(nx)==missingUpper)
v.setBit(nx);

Categories