【问题标题】:C# Looking for a more efficient means of storing and comparing lists of true/false valuesC#寻找一种更有效的方法来存储和比较真/假值列表
【发布时间】:2017-02-24 05:16:40
【问题描述】:

我有包含真/假值列表的数据集。这些列表可以是任意长度,但在大多数情况下,长度通常会在 5 到 40 项之间变化。在每个数据集中,所有列表的长度都相同。出于此过程的目的,列表中的元素一旦创建,将始终处于相同的顺序(即,一旦将列表设置为 true、false、false、true,它将始终为 true、false、false、真的)。

我还需要能够快速比较任何两个长度相等的列表中的不等式(即相同顺序的相同值)。在这种情况下,不等式是指一个数据集为真的槽在另一个数据集的相同槽中不能有任何匹配的真实值。假值无关紧要。例如:

  • 10010 和 10001 是“相等的”,因为这两个值的第一个槽是 真的
  • 00100 和 00001 “不相等”,因为没有任何真值 落在同一个位置
  • 00000 和 00000 也“不相等”,因为 两者都没有真正的价值

比较会一遍又一遍地进行,并且需要以尽可能最快和最节省内存的方式进行。对于给定的数据集,初始创建过程只会运行一次,因此比较过程的效率是次要的。

我已经尝试使用数组和排序的布尔值列表进行逐个位置的布尔比较以及字符串(“100101”格式)进行循环位置字符值比较。但似乎应该有一种处理器和内存效率更高的方式来存储和比较这些值列表。

字符串比较版本的示例。数组和列表比较遵循相同的模式:

private bool DoListsConflict(string activeValuesA, string activeValuesB)
{
     var lengths = new int[3] {10000, activeValuesA.Length, activeValuesB.Length};

     var a = activeValuesA.ToCharArray();
     var b = activeValuesB.ToCharArray();

     for (var x = 0; x < lengths.Min(); x++)
     {
         if (a[x] == '1' && b[x] == '1') return true;
     }
     return false;
}

我查看了this question,其答案建议使用 BitArrays,但建议的答案还指出它不一定有效,我不知道这会比我已经在做的更好。有没有更有效的结构可以用来加快整个流程?

【问题讨论】:

  • 显示代码,努力看看比什么更有效率。
  • 我没有添加代码,因为我觉得我在下一段中清楚地解释了我当前的流程。如果有人看到我发布一个或两个 for 循环遍历一对数组或列表来比较在网络上已被数百万次看到的值是有益的,我将很乐意这样做。
  • 添加代码示例

标签: c# list boolean comparison


【解决方案1】:

按位与并检查得到的值是零还是非零。

【讨论】:

  • 嗯,好吧,我想我明白了。我过去曾试图避免按位计算。因此,如果我理解,将每个列表存储为一个数字(uint 或 ulong),然后在每个数字之间进行按位和比较。如果结果是非零值,则存在冲突。正确的???唯一的例外是如果两个列表都是 0 值。但我可以在进行按位与之前对这种情况进行初步检查。
【解决方案2】:

最好和最有效的方法是使用位。位是计算机中较小的数据大小,也是最有效的,因为您可以使用 ULA 在不到一个机器时钟周期内处理它(当 CPU 流水线已满时)。

为此,我在下面创建了一个名为 BooleanSet 的简单类。它可以使用尽可能少的内存和最少的 CPU 时间存储任意数量的布尔值:

    public class BooleanSet
{
    private List<ulong> values = new List<ulong>();
    private int count = 0;
    /* 0x8000000000000000UL is the same as the binary number 1000000000000000000000000000000000000000000000000000000000000000 */
    private const ulong MsbFilterMask = 0x8000000000000000UL;
    /* 0xfffffffffffffffeUL is the same as the binary number 1111111111111111111111111111111111111111111111111111111111111110 */
    private const ulong LsbEraserMask = 0xfffffffffffffffeUL;
    public BooleanSet()
    {
        /* the set mut be initialized with a 0 value */
        values.Add(0);
    }

    /// <summary>
    /// Append a new boolean value to the list
    /// </summary>
    /// <param name="newValue">New value to append</param>
    public void Append(bool newValue)
    {
        /* Each element in list can store up to 64 boolean values.
         * If is number is going to be overcome, the set must grow up.
         */
        if (count % 64 == 63)
        {
            values.Add(0);
        }
        count++;
        /* now  we just have to shift the whole thing left, but we have
         * to preserve the MSB for lower elements:
         * 
         * We have to initialize the last MSB (Most Significant Bit) with the new value.
         */
        ulong lastMsb = newValue ? 0x1UL : 0x0UL;

        for (int position = 0; position < values.Count; position++)
        {
            /* & is the bitwhise operator AND
             * It is used as a mask to zero fill anything, except the MSB;
             * After get the MSB isolated, we just have to shift it to right edge (shift right 63 times)
             */
            ulong currentMsb = (values[position] & MsbFilterMask) >> 63;
            /* Now we have discart MSB and append a new LSB (Less Significant Bit) to the current value.
             * The | operator is the bitwise OR
             */
            values[position] = ((values[position] << 1) & LsbEraserMask) | lastMsb;
            lastMsb = currentMsb;
        }
        /* We don't have to take care of the last value, because we have did this already (3 1sf lines of this methid) */
    }

    public override string ToString()
    {
        /* Now we have to write the set as a string */
        StringBuilder sb = new StringBuilder();
        /* We have to keep track of the total item count */
        int totalCount = count;
        string separator = "";
        foreach (ulong value in this.values)
        {
            /* for each value in the internal list, we have to create a new bit mask */
            ulong bitMask = 1;
            /* We have to get all bits of all values, except the last one, because it may not be full.
             * the totalCount will let us to know where we reach the end of the list.
             */
            for (int pos = 0; pos < 64 && totalCount > 0; pos++, totalCount--)
            {
                /* We have to write the string in reverse order, because the first item is in the end of our list */
                sb.Insert(0, separator);
                sb.Insert(0, (value & bitMask) > 0);
                separator = ", ";
                bitMask = bitMask << 1;
            }
        }
        return sb.ToString();
    }
}

这个类只有两个方法: 1:Append(),用于向集合添加新值。 2:ToString(),用于按添加顺序列出所有存储的值。

为了测试这个例子,只需像这样创建一个控制台应用程序:

        static void Main(string[] args)
    {
        BooleanSet set = new BooleanSet();
        set.Append(false);
        set.Append(false);
        set.Append(true);
        set.Append(false);
        set.Append(true);
        set.Append(false);
        set.Append(true);

        Console.WriteLine("Checking the stored values:");
        Console.WriteLine(set.ToString());
        Console.WriteLine();
        Console.WriteLine("Checking the stored values again:");
        Console.WriteLine(set.ToString());
        Console.WriteLine();
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

输出将是这样的:


检查存储的值:
假,假,真,假,真,假,真

再次检查存储的值:
假,假,真,假,真,假,真

按任意键继续...


要比较两个集合,您只需按照您想要的方式比较其内部列表中的值。

【讨论】:

    猜你喜欢
    • 2013-11-19
    • 2023-01-19
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    • 1970-01-01
    • 2013-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多