Java manipulates large bit numbers

I’m working on the Challenge Street Challenge, changing bits in my free time a little over a week and just spin my wheels at that moment, so I hope someone can give me a pointer or a hint in the right direction.

The task is based on two bit strings A and B and the execution of several queries that manipulate two bit strings.

Let A and B be two N bit numbers. You are given initial values ​​for A and B, and you must write a program that processes three types of queries:

  • set_a idx x: set A [idx] to x, where 0 <= idx <N, where A [idx] idx'th least significant bit A.
  • set_b idx x: set B [idx] to x, where 0 <= idx <N.
  • get_c idx: print C [idx], where C = A + B and 0 <= idx

If the bit numbers are from 1 to 100,000 in length, and the program can have from 1 to 500,000 requests of any set_a, set_b or get_c combination.

To minimize the loop, I use C as a total. When the bit in or B is changed, the changed bit is also added or subtracted from C. Further minimization of the cycle when adding and subtracting is performed from the changed bit to the left hand, while the carry bit is still stored.

private static void add(final boolean[] the_array, final int the_index) { for(int iter = the_array.length - the_index - 1; iter >= 0; iter--) { if(the_array[iter]) { the_array[iter] = false; } else if(!the_array[iter]) { the_array[iter] = true; return ; } } } private static void subtract(final boolean[] the_array, final int the_index) { for(int iter = the_array.length - the_index - 1; iter >= 0; iter--) { if(the_array[iter]) { the_array[iter] = false; return ; } else if(!the_array[iter]) { the_array[iter] = true; } } } 

In general, the program works pretty well, completing the worst edge case with a bit length of 100,000 and 500,000 requests running in about 120 ms, but for the purpose of this task is still not fast enough. Due to the size of the bits, bits quickly exceed the values ​​of primitive integer (long) values ​​in Java, so most of the API is not applicable for this. Since I am not making much progress, increasing the overall amount of startup time begins to suggest that there may be an algorithm or data structure that is better suited for this than just an array. Any suggestions?

+6
source share
2 answers

Hmm, from the head:
Don't keep C around / updated, just calculate the bit you were asked for on the fly. Remember that when you add the corresponding positions of bits A and B, you only need to look down adjacent less significant bits until you find the point at which they are zero - all that can not carry bits past it. eg.

 A: 001101001010111 B: 010110100010110 ^ asked for C there 1000111^ only need to add bits from here ^ until you get your answer. 
+3
source

I would use a long [] instead of a logical []. Each long one has 64 bits, and you can perform combined operations such as

 if (the_array[iter] == -1L) // all 1s the_array[iter] = 0L; // all zeros. 

You can perform 64-bit operations at about the same time as the logical operation, making the loop up to 64x faster.

BTW: using boolean[] will use 8 times more memory than long[] , since each boolean uses memory bytes in most JVMs.


Examples of using longer types for storing data.

Depending on what operations you perform, using int may be easier to work with, for example. x = (long) a * b will overflow with long , but not int .

If you scan multiple bits (as BitSet can do), then using long[] can be faster.


Adding a benchmark showing the difference in time that it can make.

 public static final int RUNS = 100; public static void main(String... args) throws InterruptedException { boolean[] bools = new boolean[1024 * 1024]; long[] longs = new long[bools.length / 64]; for (int i = 0; i < 5; i++) { testBools(bools); testLongs(longs); } } private static void testBools(boolean[] bools) { long start = System.nanoTime(); for (int t = 0; t < RUNS; t++) { // set all to true for (int i = 0; i < bools.length; i++) bools[i] = true; // search for a false; for (boolean b : bools) if (!b) throw new AssertionError(); // set all to false for (int i = 0; i < bools.length; i++) bools[i] = false; // search for a true; for (boolean b : bools) if (b) throw new AssertionError(); } long time = System.nanoTime() - start; System.out.printf("To set and scan a boolean[] with %,d bits took %,d us%n", bools.length, time / 2/ RUNS / 1000); } private static void testLongs(long[] longs) { long start = System.nanoTime(); for (int t = 0; t < RUNS; t++) { // set all to true for (int i = 0; i < longs.length; i++) longs[i] = ~0; // all 1s // search for a false; for (long l : longs) if (l != ~0) throw new AssertionError(); // set all to false for (int i = 0; i < longs.length; i++) longs[i] = 0; // all 0s // search for a true; for (long l : longs) if (l != 0) throw new AssertionError(); } long time = System.nanoTime() - start; System.out.printf("To set and scan a long[] with %,d bits took %,d us%n", longs.length * Long.SIZE, time / 2/ RUNS / 1000); } 

prints

 To set and scan a boolean[] with 1,048,576 bits took 777 us To set and scan a long[] with 1,048,576 bits took 104 us To set and scan a boolean[] with 1,048,576 bits took 666 us To set and scan a long[] with 1,048,576 bits took 20 us To set and scan a boolean[] with 1,048,576 bits took 537 us To set and scan a long[] with 1,048,576 bits took 18 us To set and scan a boolean[] with 1,048,576 bits took 567 us To set and scan a long[] with 1,048,576 bits took 28 us To set and scan a boolean[] with 1,048,576 bits took 776 us To set and scan a long[] with 1,048,576 bits took 27 us 

In this example, it is about 30 times slower to use a boolean[] compared to a long[]

+2
source

Source: https://habr.com/ru/post/908757/


All Articles