高效的数据存储/算法来模式匹配 0 和 1 的二进制字符串

Posted

技术标签:

【中文标题】高效的数据存储/算法来模式匹配 0 和 1 的二进制字符串【英文标题】:Efficient data store / algorithm to pattern match binary strings of 0s and 1s 【发布时间】:2022-01-16 21:47:54 【问题描述】:

我生成了一个包含数百行的 .txt 文件。 每一行看起来像这样:

0000000000000000000000000000000000000000100000000010000000001000000000100000000010000000000000000000

现在我有一个输入函数,可以提交这样的字符串:

0000000000000000000000000000000000000000000011000010000000001000000000100000000010000000000000000000

如果输入字符串为 1,而 .txt 中的字符串为 0,则无关紧要。 该算法应该解决的问题如下:

在 .txt 中查找“最佳匹配”行,其中输入字符串在该行具有的相同索引处具有尽可能多的 1 输出这些“最佳匹配行”的索引为 1 而输入没有

情况:

.txt 每行最多有五个 1 输入字符串中最多可以有一百个 1。 每行和输入的长度正好是 100 个字符。 每一行和输入总是只包含 0 或 1;没有其他字符。

这是一个比实际问题更小的示例,假设长度为 6,最多 3 个(对于此示例,我将从 1 开始计算索引而不是 0):

rows.txt

000111
010101
110010
001011

输入字符串:

001110

算法的预期输出

Closest matching rows are:

Row 1: two matching 1s; your input misses a 1 on index 6
Row 4: two matching 1s; your input misses a 1 on index 4

问题/可能的解决方案:

我选择 0 和 1 来表示我需要的信息,因为我计划将它们放入二进制文件中。在代码(Java/Kotlin)中使用二进制表示和无符号整数时,我期待更好的性能和更小的文件大小。 但是继续uInt8;我需要在每一行上添加“0000”以使每行有 104 个字符长,可以被 8 整除。 这当然是可能的,但是我将所有内容都保存为整数,我只是不知道如何在字节表示级别上比较它们以基本上在代码中进行我需要的模式匹配。

为给定问题寻找性能最佳的存储/模式匹配解决方案。


这就是我这样做的原因: 我有一个带有可点击网格的前端。未选中的项目由 0 表示。单击项目并因此选择它由 1 表示。用户最多可以选择 100 个可用项目中的 50 个。但是,有些项目依赖于其他项目。每个项目总是依赖于其他 4 个项目。这些是(当前).txt 文件的行。这就是为什么我需要弄清楚缺少什么来推荐一个完全依赖于五个 1 工作的配置,而我不需要关心是否有额外的选择 (1s)。也许这个用例阐明了像这样的抽象问题如何是现实世界的问题,而不是学校作业。先前的解决方案涉及用矩阵表示网格,但使用二进制字符串似乎更有效。

澄清问题: 二进制三:

00000011

二进制十三:

00001101

如果有一行表示二进制 3,输入是二进制 13, 我怎么能比较

val row: uint8 = 3u
val input: uint8 = 13u

得到结果

missing value 2 (-> a one on index 7

【问题讨论】:

meta.***.com/questions/334822/… @sidgate 这不是一个家庭作业问题。这是一个个人项目。也少寻找一个完整的算法,多寻找关于如何处理整数二进制匹配的提示/解释。 您可以先创建算法并使用函数来检索“位”。如果可行,您可以更改内部表示并将字符串存储为位数组。在最后一步中,为了获得一些处理速度,“内联”get-bit 函数可能会很有趣。作为提示,如果我想知道字节 a 的第 5 位(从零开始)是否为 1,如果设置了该位,则像 (a & 0x20 == 0 ? 0 : 1) 这样的表达式会产生 1,否则会产生 0。 【参考方案1】:

不要使用“0”、“1”文本,使用二进制表示。

使用按位& 将“输入”与每个“行”进行比较,然后通过从此处的答案之一中获取可接受的解决方案来计算结果中的数量:Count number of 1's in binary representation。

编辑:

为了回应@gidds的评论,稍微扩展一下这个想法:

您已经表示愿意用零填充('继续使用 uInt8;我需要添加“0000”'),所以也许可以使用 int(太糟糕了,每行的长度都不是 96!),为了方便在一个原始操作中比较 32 列,它交换了一些空间。实际上它不会浪费太多空间,因为您可以将文件的每一行循环读取到一个可重复使用的 ByteBuffer 中。 ByteBuffer#allocate 已经初始化为零,因此需要注意填充。没有理由你不能使用 longintbyte 总共 104 位,但这可能不值得麻烦 - Java/Kotlin 中没有蚕食,所以没有办法得到正好 100 个。我选择了 4 个ints 而不是 2 个longs,只是为了能够利用上面链接中的bitCount 算法。

这是一个相当全面的纯 Java 草图:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

public class BitSimilarityChecker 
    
    public void main(String[] args) throws Exception 
        
        // int[] input = ... 
        assert input.length == 4;
        
        try (ByteFileReader reader = new ByteFileReader(args[0])) 
            while (/* TODO: When has file been fully read? */) 
                int[] row = reader.read(); 
                assert row.length == 4;
                int sum = 0;
                for (int i = 0; i < 4; ++i) 
                    sum += bitCount(and(input[i], row[i]));
                
                // TODO: Keep track of the max sum, row pair...
            
        
    
    
    private static int and(int left, int right) 
        return left & right;
    
    
    // Taken from https://***.com/a/8871435 - unverified.
    private static int bitCount(int u) 
         int uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
         return ((uCount + (uCount >> 3)) & 030707070707) % 63;
    
    
    private static class ByteFileReader implements AutoCloseable 

        private static final int CAPACITY = 4 * 8 * Integer.BYTES;
        private static final int LIMIT = 100;
        
        private final ReadableByteChannel channel;
        private final ByteBuffer buffer;
        
        @SuppressWarnings("resource")
        public ByteFileReader(String file) throws FileNotFoundException 
            channel = Channels.newChannel(new FileInputStream(file));
            buffer = ByteBuffer.allocate(CAPACITY);
        
        
        public int[] read() throws IOException 
            buffer.position(0);
            buffer.limit(LIMIT);
            int bytesRead = channel.read(buffer);
            buffer.limit(CAPACITY);
            // TODO: Check bytesRead to see the status of the read.
            // Pass this information on to the caller somehow. 
            return new int[] 
                    buffer.getInt(),
                    buffer.getInt(),
                    buffer.getInt(),
                    buffer.getInt()
            ;
        

        @Override
        public void close() throws Exception 
            channel.close();
        
    

【讨论】:

二进制表示有什么类型?根据问题,每行有 100 个字符,所以每个数字有 100 位——对于Long(64 位)或任何其他原始类型来说太大了。 Kotlin 没有提供可以使用按位 &amp; 运算符的更大类型。 (如果在 JVM 上运行,你可以使用java.util.BitSet,虽然语法更尴尬。)

以上是关于高效的数据存储/算法来模式匹配 0 和 1 的二进制字符串的主要内容,如果未能解决你的问题,请参考以下文章

字符串的模式匹配算法

浅析区块链中的HASH算法

区块链解读-区块链中HASH算法

串串

数据结构-串的模式匹配

数据结构(c++)字符串 模式匹配算法问题,对高手来说只要写一点点