我正在寻找C中项目的数据结构来存储列表列表。我需要能够访问仅给定n的第n个列表(这些术语将被无序访问)。单个列表将包含1到M个整数(比如M = 25表示具体性);外部列表包含N个。单个列表的平均值接近于1:在我的示例中,只有20%的列表具有5到25个元素。
显而易见的实现是长度为N * M的数组。但这是空间效率低下的:出于性能原因,结构不占用太多内存非常重要。有什么好办法呢?
我正在写一个分解筛。外部数组表示从S b + 1到S (b + 1)的数字,并且每个数组存储该范围内一个数的素因子。结构越小,可以选择更大的S,减少(昂贵的)划分的数量。
这也为优化提供了另一种途径:只存储大于或等于L的素数。好处是不需要每个列表中的楼层(log_2(x =范围内的最大数字))元素,而只需要楼层( log_L(X))。 (上面的例子对应于x = 10 ^ 12,L = 3.)缺点是重构分解,需要对L以下的素数进行试分。
在我的应用程序中,每个因子分解都被重建一次,所以在我的例子中,将L增加到下一个主要成本(稍微多于)10 ^ 12个额外的分区;在3 GHz K10上,这是一个数量级,每个24-87个操作或总共2-8个小时。内存结构越有效,我需要花费2到8小时的时间。 (另一方面,占用过多CPU工作量的内存结构不值得,除非它们提供更好的权衡。)
答案 0 :(得分:2)
我想到的一个数据结构是一个二维数组,其中'inner'数组比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_t
s可以存储小于3 * 5 * 7 * 11 = 1155(11位)的因式数字。
使用3 uint32_t
s可以存储小于3 * 5 * 7 * 11 * 13 = 15015(14位)的因式数字。
使用4 uint32_t
s可以存储小于3 * ... * 17 = 255255(18位)的因子数。
使用5 uint32_t
s可以存储小于3 * ... * 19(23位)的因子数。
使用6 uint32_t
s可以存储小于3 * ... * 23(27位)的因子数。
使用7 uint32_t
s可以存储小于3 * ... * 29(32位)的因子数字。
使用8 uint32_t
s可以存储小于3 * ... * 31(37位)的因子数字。
使用9 uint32_t
s可以存储小于3 * ... * 37(42位)的因式数字。
使用10 uint32_t
s可以存储小于3 * ... * 41(48位)的因子数。
使用11 uint32_t
s可以存储小于3 * ... * 43(53位)的因子数。
使用12 uint32_t
s可以存储小于3 * ... * 47(59位)的因式数字。
使用13 uint32_t
s可以存储小于3 * ... * 53(64位)的因子数。
使用14 uint32_t
s可以存储小于(2 ^ 32 + 15)^ 2(64位)的因子数。
除此之外,您可能希望使用我提到的备用数据结构(辅助数组使用uint64_t
),因此您无需将主数组转换为uint64_t
。但这只是分段筛分的关注点;将所有数字筛选到2 ^ 64是不可行的 - 这需要数百年的时间。