在 C# 中存储/比较 x 数量的三元 (?) 值的内存高效方法



我有一个实体列表,为了分析的目的,实体可以处于三种状态之一。当然,我希望它只有两个状态,然后我可以用布尔值来表示它。

在大多数情况下,会有一个实体列表,其中列表的大小通常为 100<500。>

我正在努力分析实体和状态组合的影响。

因此,如果我有 1 个实体,那么我可以有 3 个组合。如果我有两个实体,我可以有六个组合,依此类推。

由于组合的数量,暴力破解是不切实际的(它需要在单个系统上运行)。我的任务是找到可行的好但不一定是最佳的解决方案。我不需要测试所有可能的排列,我只需要找到一个有效的排列。这是一个实施细节。

我需要做的是注册我当前数据集可能的组合 - 这基本上是为了避免重复分析每个组合的工作。每次进程到达特定的组合配置时,它都需要检查该组合是否已经在处理中,或者它是否在过去被解决过。

因此,如果我有 x 数量的三态值,那么在内存中存储和比较它的有效方法是什么?我意识到这里会有限制。只是想尽可能提高效率。

我想不出比两位更有效的存储单元,其中不使用四个"位状态"之一。但我不知道如何提高效率。我是否需要选择优化存储大小或性能?

如何在 C# 中以浪费最少资源的方式建模这样的事情,并且在进程需要询问"这种特殊的三态值组合是否已经过测试?"时仍然表现得相对较好?

编辑:例如,假设我只有 3 个实体,状态由一个简单的整数 1、2 或 3 表示。然后,我们将有以下组合列表:

111 112 113 121 122 123 131 132 133 211 212 213 221 222 223 231 232 63· 311 312 313 321 322 323 331 332 333

我认为您可以将其分解如下:

  1. 您有一组 N 个实体,每个实体可以具有三种不同状态之一。
  2. 给定这 N 个实体的一种特定状态排列,则 想要记住你已经处理了这种排列。

因此,您似乎可以将 N 个实体视为具有 3 位数字的以 3 为底的数字。

在考虑 N 个实体的一组特定状态时,可以将其存储为 N 个字节的数组,其中每个字节可以具有值 0、1 或 2,对应于三种可能的状态。

这不是存储一个特定排列的状态的内存高效方法,但这没关系,因为您不需要存储该数组。您只需要在与该排列对应的某个位置存储一个位。

所以你可以做的是将字节数组转换为一个基数为 10 的数字,你可以用它作为索引到BitArray。然后,使用该BitArray来记住是否已处理特定的状态排列。

若要将表示三进制数的字节数组转换为十进制数,可以使用以下代码:

public static int ToBase10(byte[] entityStates)  // Each state can be 0, 1 or 2.
{
int result = 0;
for (int i = 0, n = 1; i < entityStates.Length; n *= 3, ++i)
result += n * entityStates[i];
return result;
}

假设您有numEntities不同的实体,则可以创建一个如下所示的BitArray

int numEntities = 4;
int numPerms = (int)Math.Pow(numEntities, 3);
BitArray states = new BitArray(numPerms);

然后states可以为所有实体的每个可能的状态排列存储一个位。

假设您有 4 个实体 A、B、C 和 D,并且您有一个状态排列(将是 0、1 或 2),如下所示:A2 B1 C0 D1。也就是说,实体 A 具有状态 2,B 具有状态 1,C 具有状态 0,D 具有状态 1。

您可以将其表示为布尔数组,如下所示:

byte[] permutation = { 2, 1, 0, 1 };

然后,您可以将其转换为以 10 为基数的数字,如下所示:

int asBase10 = ToBase10(permutation);

然后,您可以检查该排列是否已像这样处理:

if (!bits[permAsBase10])
{
// Not processed, so process it.
process(permutation);
bits[permAsBase10] = true; // Remember that we processed it.
}

不要过于花哨地使用算法和数据结构,并假设您的三态值可以用字符串表示,并且没有容易确定的固定最大数量。 即。 "111"、"112"等(甚至"1:1:1"、"1:1:2"),那么一个简单的 SortedSet 最终可能会相当高效。

作为奖励,它不关心集合中的值数量。

SortedSet<string> alreadyTried = new SortedSet<string>();
if(!HasSetBeenTried("1:1:1"){   
// do whatever  
}
if(!HasSetBeenTried("500:212:100"){   
// do whatever  
}
public bool HasSetBeenTried(string set){
if(alreadyTried.Contains(set)) return false;
alreadyTried.Add(set);
return true;
}

简单的数学 说:

3 个州的 3 个实体构成 27 种组合。 所以你需要确切的 log(27)/log(2) = ~ 4.75 位来存储这些信息。

因为一台电脑只能利用整个位,所以你需要"浪费"~0.25位,每个组合使用5位。

您收集的数据越多,您就越能更好地打包这些信息,但最终,也许压缩算法可以提供更多帮助。

再说一遍:你只要求内存效率,而不是性能。

一般来说,你可以通过Math.Ceil(Math.Log(noComposites, 2))计算你需要的位。

最新更新