【问题标题】:Storing list of lists efficiently (sieving)有效地存储列表列表(筛选)
【发布时间】:2016-02-02 12:21:11
【问题描述】:

问题

我正在为 C 中的项目寻找一种数据结构来存储列表列表。我需要能够访问仅给出 n 的第 n 个列表(这些术语将被无序访问)。单个列表将包含 1 到 M 之间的整数(例如 M = 25 表示具体);外部列表包含其中 N 个。平均而言,单个列表比 M 更接近 1:在我的示例中,只有 20% 的列表具有 5 到 25 个元素。

显而易见的实现是一个长度为 N*M 的数组。但这是空间效率低下的:出于性能原因,结构不要占用太多内存很重要。有什么好的方法可以做到这一点?

上下文

我正在编写分解筛。外部数组表示从 Sb + 1 到 S(b+1) 的数字,每个数组都存储该范围内一个数字的质因数。结构越小,可以选择的 S 越大,从而减少(昂贵的)划分的数量。

这也提供了另一种优化途径:只存储大于或等于 L 的素数。好处是每个列表中不需要 floor(log_2(x = maximum number in range)) 元素,您只需要 floor( log_L(x))。 (上面的例子对应于 x = 10^12, L = 3。)缺点是重构分解需要对 L 以下的素数进行试除。

在我的应用程序中,每个因式分解都重构一次,因此在我的示例中,将 L 增加到下一个素数成本(略多于)10^12 个额外的除法;作为一个数量级,这在 3 GHz K10 上是每个 24-87 个操作或总共 2-8 小时。内存结构越高效,我需要花费的 2 到 8 个小时的时间就越少。 (另一方面,占用过多 CPU 工作的内存结构不值得,除非它们提供更好的权衡。)

【问题讨论】:

  • 为什么不稍微作弊并持有一个包含 100 个第一个已知素数的小列表呢?你必须处理多大的 N?
  • 为什么不存储一个包含 N 个指向列表的指针的数组?
  • 预计 M 或 N 会更大吗?你能给出这些值的大致范围吗?
  • link 是一个设计良好的 C 数组列表实现的参考,它可能会为您省去使用 sieve.at 的麻烦
  • @GiladMitrani:N 通常在 10^5 到 10^8 左右,具体取决于高速缓存和主内存的速度/大小。如果要检查高达 10^12 的范围,这将需要 10^4 到 10^7 次程序调用。

标签: c math optimization data-structures prime-factoring


【解决方案1】:

想到的一个数据结构是一个二维数组,其中“内部”数组比 floor(log_L(x)) 小很多。如果有剩余的主要因素,则在最后一个元素中存储一个指针,该指针将转到辅助溢出数组。您还可以通过省略最后一个主要因素来减少所需的存储空间,这可以通过除掉其他因素来确定。

我不知道这是否比天真的方法好得多。好处是内存使用量要小得多——可能是 5 个元素而不是 25 个,让您在同一空间中装入 4 到 5 倍的数字。缺点是重建数字的工作量更大,并且内存局部性可能会稍差。

但还有另一个技巧可能会有所帮助。只要 L > 2,列表中的所有数字都是奇数,因此最后一位是未使用的。您可以使用它来将指数存储在数字本身中:存储 p * 2^(e-1) 而不是 p。所以“3”代表3,“6”代表3^2,“12”代表3^3,以此类推。如果您使用 64 位数字,您可以将 3^63 表示为 3*2^62,它小于 2^64。 (更大的基数更容易:5^62 比 3^63 大 18 万亿倍,但可以用这种格式用 64 位表示。)32 位将您限制为 3^31,但您已经不能表示素数 2^ 32 + 15,这使得限制略大于 2^64。

实际上,我认为这已经足够好,可以完全跳过辅助数组。这是一个列表,显示您需要存储多少个因子

使用 1 uint32_t 可让您存储小于 3*5*7 = 105(7 位)的因式数。

使用 2 uint32_ts 可让您存储小于 3*5*7*11 = 1155(11 位)的因式数。

使用 3 uint32_ts 可让您存储小于 3*5*7*11*13 = 15015(14 位)的因数。

使用 4 uint32_ts 可让您存储小于 3*...*17 = 255255(18 位)的因数。

使用 5 uint32_ts 可让您存储小于 3*...*19(23 位)的因式数。

使用 6 uint32_ts 可让您存储小于 3*...*23(27 位)的因式数。

使用 7 uint32_ts 可让您存储小于 3*...*29(32 位)的因数。

使用 8 uint32_ts 可让您存储小于 3*...*31(37 位)的因式数。

使用 9 uint32_ts 可让您存储小于 3*...*37(42 位)的因式数。

使用 10 uint32_ts 可让您存储小于 3*...*41(48 位)的因式数。

使用 11 uint32_ts 可让您存储小于 3*...*43(53 位)的因数。

使用 12 uint32_ts 可让您存储小于 3*...*47(59 位)的因数。

使用 13 uint32_ts 可让您存储小于 3*...*53(64 位)的因式数。

使用 14 uint32_ts 可以存储小于 (2^32 + 15)^2(64 位)的因式数。

要超越这一点,您可能需要使用我提到的备用数据结构(辅助数组使用uint64_t),因此您不需要将主数组转换为uint64_t。但这仅与分段筛分有关;将所有数字筛选到 2^64 是不可行的——这需要数百年。

【讨论】:

  • +1 我在考虑大致相同的数据结构。不过,看看当地是否会毁掉它真的很有趣。
猜你喜欢
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多