C# 松散组合比较

C# 松散组合比较,c#,winforms,performance,string-comparison,C#,Winforms,Performance,String Comparison,比较两种组合的最佳方法是什么 我的情况: 我目前有一个字符串string1=“xxxxxx”。长度为6个字符。每个字符值为0或1或x。我需要将此字符串与另一个字符串进行比较,该字符串具有相同数量的字符,但值不是1就是0 第一个字符串中的charx表示第二个字符串中的char值 string可以是任何内容 字符0在第一个string中-仅接受第二个string中的0 第一个字符串中的字符1-第二个字符串中仅接受1 下面是一个简单的例子: string pattern = 'xxxxxx'; s

比较两种组合的最佳方法是什么

我的情况:

我目前有一个
字符串string1=“xxxxxx”。长度为
6个
字符。每个字符值为
0
1
x
。我需要将此
字符串
与另一个
字符串
进行比较,该字符串具有相同数量的字符,但值不是
1就是
0

  • 第一个字符串中的char
    x
    表示第二个字符串中的char值
    string
    可以是任何内容

  • 字符
    0
    在第一个
    string
    中-仅接受第二个
    string
    中的
    0

  • 第一个
    字符串中的字符
    1
    -第二个
    字符串中仅接受
    1

下面是一个简单的例子:

string pattern = 'xxxxxx';
string test1 = '010101';
// pass

string pattern = '1xxxxx';
string test2 = '010101';
// not pass

string pattern = '0xxxxx';
string test3 = '010101';
// pass
我为它做了一个函数:

    public bool passCombination(string pattern, string combination)
    {
        bool combination_passed = true;
        for (int i = 0; i < pattern.Length; i++)
        {
            char test_char = pattern[i];
            if (test_char != 'x' && combination[i] != test_char)
            {
                combination_passed = false;
                break;
            }
        }

        return combination_passed;
    }
公共bool密码组合(字符串模式、字符串组合)
{
布尔组合_通过=真;
for(int i=0;i
这很简单。基本上,我是在
char
之后对
char
进行评分。如果它是
x
,那么我不关心第二个字符串中的值。如果是其他字符,则进行比较

因为这是一个基于字符串的方法,我在考虑其他的解决方案,也许?在我的真实场景中,我必须执行大约(~700k次此类检查*~150万次)。这个非常乐观的数字:)

我在考虑
regex
比较,或者将所有组合保存到
int[]
数组中并进行比较。或者,也许可以使用
散列来实现一些神奇的效果

因此,至少还有3个其他选项可以提高性能。。有人能提出更高性能的解决方案吗

编辑:


我确实做了对比测试。使用旧的aproach,我得到了2.5分钟的执行时间,而使用下面建议的新aproach(接受答案)-大约2分钟。大约是性能提高了20%

首先,在浪费时间编写过于聪明的代码以节省浪费的周期之前,确保您确实需要优化任何东西

但如果您确实需要优化,您可以随时调整一些位。它通常比循环浏览东西要快,在极少数情况下,如果不是这样的话,阅读代码的人会觉得它更快

警告:如果您永远不会或很少会对任何给定的“值”字符串进行多次比较,那么这种方法没有好处,因为编译无论如何都会涉及到字符串的循环

如果确实存在性能问题,可以将模式“编译”为两个整数:一个是每个
1
都有
1
的模式,另一个是每个
0
x
都有
0
;另一个是一个掩码,每个
x
使用
0
,每个
0
1
使用
1
。每个整数浪费26位,但我不会告诉任何人

然后将值编译成整数:
1
表示
1
0
表示
0

编写一个具有这些模式/掩码int的类,以及一个将它们与值int进行比较的方法。如果需要显示它们,可以“预编译”这些“值”,并将它们存储为整数而不是字符串,也可以是一个具有int属性和string属性的类(或者您可以编写一个函数将这些整数转换回字符串)

公共类模式匹配器
{
公共模式匹配器(字符串模式)
{
模式=编译模式(模式);
掩码=编译掩码(模式);
}
#区域字段
//我们可以通过生成这些字段而不是属性来保存任何周期吗?
//我认为优化器比这更聪明。
公共int模式{get;private set;}
公共整数掩码{get;私有集;}
#端域字段
公共布尔校验值(字符串值)
{
返回CheckValue(CompileValue(value));
}
公共布尔校验值(int值)
{
//a&b:按位和
//两个数字中的任何“真”位在结果中都是“真”。
//任何一个数字中的“false”位在结果中都是“false”。
//      11 & 11 == 11
//      11 & 01 == 01
//      11 & 10 == 10
//      11 & 00 == 00
//      01 & 11 == 01
//      01 & 01 == 01
//      01 & 10 == 00
//      01 & 00 == 00
//所以xx0011->
//图案:000011
//口罩:001111
//价值110011
//  (110011 & 001111) == 000011
//  (000011 & 001111) == 000011
//
//000011==000011,所以这两个匹配。
返回值(值和掩码)=(图案和掩码);
}
公共静态整型编译任务(字符串模式字符串)
{
int-mask=0;
int位偏移=0;
//对于patternString中的每个字符,在掩码中设置一位。
//从零位开始,每个字符向左移动一位。
//在x86上,这些位与中的字符顺序相反
//字符串,但这不重要。
foreach(模式字符串中的变量ch)
{
开关(ch)
{
//如果模式有“0”或“0”,我们将对此进行检查
//值中的字符,因此在遮罩中的该点处放置1。
案例“1”:
案例“0”:
//a | b:按位或:如果一位在任一数字中为“真”,则为
//结果为真。因此0110 | 1000==11
public class PatternMatcher
{
    public PatternMatcher(String pattern)
    {
        Pattern = CompilePattern(pattern);
        Mask = CompileMask(pattern);
    }

    #region Fields
    //  Could we save any cycles by making these fields instead of properties? 
    //  I think the optimizer is smarter than that. 
    public int Pattern { get; private set; }
    public int Mask { get; private set; }
    #endregion Fields

    public bool CheckValue(String value)
    {
        return CheckValue(CompileValue(value));
    }

    public bool CheckValue(int value)
    {
        //  a & b: Bitwise And
        //      Any bit that's "true" in both numbers is "true" in the result. 
        //      Any bit that's "false" in EITHER number is "false" in the result.

        //      11 & 11 == 11
        //      11 & 01 == 01
        //      11 & 10 == 10
        //      11 & 00 == 00

        //      01 & 11 == 01
        //      01 & 01 == 01
        //      01 & 10 == 00
        //      01 & 00 == 00

        //  So xx0011 -> 
        //      Pattern: 000011
        //      Mask:    001111
        //      Value    110011

        //  (110011 & 001111) == 000011
        //  (000011 & 001111) == 000011
        //
        //  000011 == 000011, so these two match. 

        return (value & Mask) == (Pattern & Mask);
    }

    public static int CompileMask(string patternString)
    {
        int mask = 0;
        int bitoffset = 0;

        //  For each character in patternString, set one bit in mask.
        //  Start with bit zero and move left one bit for each character.
        //  On x86, these bits are in reverse order to the characters in 
        //  the strings, but that doesn't matter. 
        foreach (var ch in patternString)
        {
            switch (ch)
            {
                //  If the pattern has a '0' or a '0', we'll be examining that 
                //  character in the value, so put a 1 at that spot in the mask.
                case '1':
                case '0':
                    //  a | b: Bitwise OR: If a bit is "true" in EITHER number, it's 
                    //  true in the result. So 0110 | 1000 == 1110.
                    //  a << b: Bitwise left shift: Take all the bits in a and move 
                    //  them leftward by 1 bit, so 0010 << 1 == 0100. 
                    //
                    //  So here we shift 1 to the left by some number of bits, and 
                    //  then set that bit in mask to 1. 
                    mask |= 1 << bitoffset;
                    break;

                //  If it's an 'x', we'll ignore that character in the value by 
                //  putting a 0 at that spot in the mask. 
                //  All the bits are zero already.
                case 'x':
                    break;

                default:
                    throw new ArgumentOutOfRangeException("Invalid pattern character: " + ch);
            }

            ++bitoffset;
        }

        return mask;
    }

    public static int CompilePattern(string patternString)
    {
        int pattern = 0;
        int bitoffset = 0;

        foreach (var ch in patternString)
        {
            //  For each character in patternString, set one bit in pattern.
            //  Start with bit zero and move left one bit for each character.
            switch (ch)
            {
                //  If the pattern has a 1, require 1 in the result.
                case '1':
                    pattern |= 1 << bitoffset;
                    break;

                //  For 0, require 0 in the result.
                case '0':
                    //  All the bits were zero already so don't waste time setting 
                    //  it to zero. 
                    break;

                //  Doesn't matter what we do for 'x', since it'll be masked out. 
                //  Just don't throw an exception on it. 
                case 'x':
                    break;

                default:
                    throw new ArgumentOutOfRangeException("Invalid pattern character: " + ch);
            }

            ++bitoffset;
        }

        return pattern;
    }

    public static int CompileValue(string valueString)
    {
        int value = 0;
        int bitoffset = 0;

        //  For each character in patternString, set one bit in mask.
        //  Start with bit zero and move left one bit for each character.
        foreach (var ch in valueString)
        {
            switch (ch)
            {
                //  If the value has a '1', have a 1 for that bit
                case '1':
                    value |= 1 << bitoffset;
                    break;

                //  If the value has a '0', leave a 0 for that bit
                //  All the bits were zero already.
                case '0':
                    break;

                default:
                    throw new ArgumentOutOfRangeException("Invalid pattern character: " + ch);
            }

            ++bitoffset;
        }

        return value;
    }
}
foreach ( string pattern in patterns )
{
    // save the non x indexes
    var indexes = new List<int>(); 
    for (int i = 0; i < pattern.Length; i++)
        if (pattern[i] != 'x')
            indexes.Add(i);

    foreach ( string combination in combinations )
    {
        bool combination_passed = true;
        foreach (int i in indexes)
        {
            if (combination[i] != pattern[i])
            {
                combination_passed = false;
                break;
            }
        }
        // ...
    }
}