如何使用按位运算(性能优化)检查排列的十六进制整数是否与另一个十六进制整数匹配

时间:2019-01-30 02:08:24

标签: java android algorithm kotlin bitwise-operators

previous question中讨论了部分代码优化问题并获得了一些底层改进之后,@ SergGr建议我创建一个新问题以尝试进行高层改进并达到我的主要目标目标。

在我的代码中,我必须在“加扰的” square-1 puzzle对象中应用一些动作,然后检查它是否已解决。当我发现better implementation of a square-1 puzzle object around internet时,便将其用于我的Android应用程序中。

然后,如您所见,class由四个简单数字(integer)字段构成:

var ul = 0x011233
var ur = 0x455677 //continuation of ul

var dl = 0x998bba
var dr = 0xddcffe //continuation of dl

这些字段表示Square-1拼图的“部分”,其中hexadecimal表示形式中的每个数字都是这些部分之一(两位数字,例如1133bb ...表示多维数据集的相同“部分”,其中“较大”部分)。

正如你所看到的那样,一些通过拼图来表示真实动作的操作是使用该数字进行的,但最多只能通过其位进行排列。

在我的应用程序中,我必须检查难题是否已解决,但就我的情况而言,已解决状态不一定等于原始字段。当我们谈论Rubik的Cube变体时,可以移动面孔,我们可以拥有一个已解决的状态,例如(ul +“” + ur):[112334 556770][233455 677011][455677 11233*]等上。

Obs .:注意前导零...

然后,我想到了一种简单的算法,可以通过使用固定的求解序列(但使用Strings)查找循环排列来检查求解状态。但是,经过一些测试后,我意识到使用Strings进行这项工作会使我的应用程序运行得如此缓慢,以至于我不得不在一个大循环中多次调用此检查。

关于我使用字符串的算法的简要说明:

  • 将所有数字转换为String(在本例中为十六进制字符串);
  • 固定的已解决状态(ul+ur):s = "011233"+"455677";
  • 要检查是否解决的状态(ul+ur):t = "233455"+"677011";
  • 如果我们合并s+s,则检查是否可以在t内找到"011{233455677011}233455677",然后isSolved()检查是否返回true

我必须在哪里应用

基本上,我的应用程序的核心代码是这个,然后检查是否像这样解决难题:

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
}

如您所见,这里仅使用了我创建的2个功能。我将在下面介绍方法applySequence()

applySequence()方法

我自己制作了此方法,将格式化的序列应用于Square-1拼图对象。一旦这个谜题的正式序列由一些字符组成(看起来像:“ (1, 2) / (6, -3) /”,我就采取了一些步骤来提取数字,然后移动立方体。看起来像:

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()
}

这种方法非常简单,但是考虑到它使用了Stringconcat()之类的contains()操作,因此在Android中的循环性能降低了60万倍。

尝试优化之后,我意识到最好的方法之一是按位操作,这种操作是作者对原始代码进行的操作,但是我无法弄清楚。

作为有关该问题的额外信息,如果我删除对isSolved()方法的调用,那么最终的搜索时间将在20~30 seconds中减少。

然后,如何通过比特比特化来执行isSolved()操作,以便在大的for循环中更轻便地工作(针对Android)?

1 个答案:

答案 0 :(得分:0)

您可以通过按位运算检查两个值是否彼此循环移位。

您的值是6个字节(48位长度)的值,因此我们可以将它们打包在64位long中,然后对一个值进行低48位的循环旋转,并检查与另一个值的一致性。

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