问题
After discussing a little about optimization of a part from my code in a previous question and getting some low-level improvements, @SergGr suggested me creating a new question to try get a high-level improvement and reach my main goal.
In my code I have to apply some moves in a "scrambled" square-1 puzzle object and, after, check if it was solved. As I found a better implementation of a square-1 puzzle object around internet I took it to be used in my Android application.
Then, as you can see, the class is built with four simple numbers (integer
) fields:
var ul = 0x011233
var ur = 0x455677 //continuation of ul
var dl = 0x998bba
var dr = 0xddcffe //continuation of dl
These fields means the "pieces" of the square-1 puzzle, where each digit in the hexadecimal
representation is one of these pieces (double digits such as 11
, 33
, bb
... means the same "piece" of the cube, wich is a "big" piece).
As ou can see, some operations wich represents real actions through the puzzle are made using that numbers, but at the most is permutations through its bits.
In the case of my application I have to check if the puzzle is solved but, for my situation, the solved state is not, necessarly, equals to the raw fields. As we talking about a Rubik's Cube variant the faces could be moved, the we can have a solved state like (ul + " " + ur): [112334 556770]
, [233455 677011]
, [455677 11233*]
and so on.
Obs.: note to leading zero...
Then I thought a simple algorithm to check the solved state by looking for cyclic permutations in a fixed solved sequence, but using Strings
. However, after some tests I realized that this work, using Strings
as I did, was making my app still so slow, once I have to call this checking thousand times in a big loop.
Quick explanation about my algorithm using Strings:
- Convert all numbers to
String
(in this case a hex String); - A fixed solved state (
ul+ur
):s = "011233"+"455677"
; - A state to check if is solved (
ul+ur
):t = "233455"+"677011"
; - If we concat
s+s
we check if we can foundt
inside:"011{233455677011}233455677"
, thenisSolved()
checking returnstrue
.
Where I have to apply this
Basically the core code of my app is this and I check if the puzzle is solved like this:
for (sequence in sequences) { //814*814 elements
auxCube.applySequence(sequence.seq) //applies the current sequence to the auxiliary cube
if (auxCube.isSolved()) { //checks if it was solved
searches.add(Search(sequence.seq)) //adds the sucess in a list
}
auxCube.copy(oldCube) //back test cube to initial state to continue finding sequences
}
As you can see, there are only 2 functions made by me being used here. I'll describe the method applySequence()
bellow.
The applySequence()
method
I made this method by myself to apply formated sequences to the square-1 puzzle object. Once official sequences for this puzzle are composed by some characters (looks like: "(1, 2) / (6, -3) /
" I did some steps to extract the numbers and, after, move the cube. It looks like:
fun applySequence(sequence: String) {
//remove all "(", ")", "," and " " characters and split in each "/"
val pairs = sequence.replace(" ", "").replace("\\(", "").replace("\\)", "").split("/")
//if starts with / then perform respective move
if (sequence.startsWith("/")) slashMove()
//iterate over pairs
for (pair in pairs) {
//checks if pair is not empty
if (pair != "") {
//split pair in the "," character
val pairMoves = pair.split(",")
//perform top and bottom moves with extracted numbers
move(true, Integer.parseInt(pairMoves[0]))
move(false, Integer.parseInt(pairMoves[1]))
slashMove()
}
}
//if sequence not ends with / then applies respective move
if (!sequence.endsWith("/")) slashMove()
}
This way is so simple but considering it uses String
operations like concat()
and contains()
in a for that loops over 600000 times the performance, in Android, is reduced.
After trying optimize I realized that one the best ways to do it cames with bitwise operations, operations such the author did over his original code, however I couldn't figure it out.
As a extra info about the problem, if I remove the call of my isSolved()
method the final time of the search reduces in 20~30 seconds
.
Then, how to perform isSolved()
operation using bitewising to work lighter in a big for loop (targeting Android)?
回答1:
You can check if two values are cyclic shift of each other with bitwise operations.
Your values are 6-byte (48 bits length) values, so we can pack them in 64-bit long
, then make cyclic rotation of low 48 bits for one value and check for coincidence with other one.
Ideone
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
long a = (0x011233l) << 24 | 0x455677;
long b = (0x233455l) << 24 | 0x677011;
System.out.printf("a: %x b: %x \n", a, b);
if (b == a)
System.out.printf(" bingo at zero digits shift \n");
for (int sh=4; sh<48; sh+=4) {
long bb = (b >> sh)|((b << (64 - sh))>>16);
System.out.printf("rotated %d bits bb: %x \n", sh, bb);
if (bb == a)
System.out.printf(" bingo at %d digits shift \n", sh/4);
}
}
}
a: 11233455677 b: 233455677011
rotated 4 bits bb: 123345567701
rotated 8 bits bb: 112334556770
rotated 12 bits bb: 11233455677
bingo at 3 digits shift
rotated 16 bits bb: 701123345567
skipped
rotated 44 bits bb: 334556770112
来源:https://stackoverflow.com/questions/54432393/how-to-check-if-permuted-hexadecimal-integers-matches-to-another-using-bitwise-o