How to check if permuted hexadecimal integers matches to another using Bitwise operations (performance optimization)

谁都会走 提交于 2019-12-23 02:36:25

问题


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 found t inside: "011{233455677011}233455677", then isSolved() checking returns true.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!