【问题标题】:Store many numbers as a single unique number将许多数字存储为一个唯一的数字
【发布时间】:2017-06-03 16:54:42
【问题描述】:

我有必要将许多数字(我可以决定哪些数字)存储为一个唯一的数字,我应该能够从中检索原始数字。
我已经知道两种方法:

1) 算术基本定理(素数)
假设我有 5 个值,我为每个值分配一个非 1 的素数

a = 2
b = 3
c = 5
d = 7
e = 13

如果我想存储 a、b 和 c,我可以将它们相乘2*3*5=30,并且我知道没有其他素数乘积可以是 30。然后检查一个值是否包含,例如,b,我需要做的就是30 % b == 0

2) 位掩码 就像 Linux 权限一样,使用 2 的幂并对每个值求和

但这两种方法发展迅速(第一种比第二种快),而且使用素数需要我有很多素数。
例如,当您拥有一千个值时,还有其他方法可以有效地做到这一点吗?

【问题讨论】:

  • 你对这个问题有更多的背景吗?一般来说,它不能比位掩码选项更有效,因为在您的示例中,您将有 2^5 可能性,这需要 5 位才能存储所有这些选项。但我很好奇哪里需要将它存储在一个数字中。
  • 我需要通过 JWT(JSON Web 令牌)发送权限,因为我可以为同一个用户(大型应用程序)获得数百个权限,我想知道是否有一些方法可以编写它们以紧凑的方式避免发送大令牌。默认情况下,我将它们作为权限名称数组获取,所以我在考虑替代方案,ID 数组已经更好,但我想知道是否有更好的方法。如果令牌不能超过 64 位,我可能最终只会从令牌中删除权限
  • 我对 JWT 不熟悉,但是对于数百个权限,您只需要数百位,可能不会很多?也许您可以找到一种不同的方式来存储这些位(例如多个 64 整数)并获得适合您的解决方案。

标签: php numbers primes bitmask


【解决方案1】:

如果您要存储以 10 为基数的数字,则通过以 11 为基数的数字进行转换。随着基数的增加,你有一个额外的“数字”。使用该数字作为分隔符。因此,三个基数为 10 的数字“10、42、457”变成了“10A42A457”:一个基数为 11 的数字(附加数字为“A”)。

无论您的原始数字是什么基数,都将基数加 1 并连接起来,使用额外的数字作为分隔符。这将在增加的基数中为您提供一个数字。

该单个数字可以存储在您认为方便的任何数字基中:例如二进制、十进制或十六进制。

要检索您的原始数字,只需转换为基数 11(或其他)并用分隔符替换多余的数字。

ETA:您不必使用基数 11。单个数字“10A42A457”也是有效的十六进制数,因此可以使用任何 11 或以上的基数。 Hex 可能比 base 11 更容易使用。

【讨论】:

    【解决方案2】:

    例如,当您拥有 一千个值时,是否有任何其他方法可以有效地做到这一点?

    我不是数学家,但这是基础数学,一切都取决于范围

    范围 0-1:您要存储 4 个数字 0-1 - 它基本上是二进制系统

    Number1 + Number2 * 2^1 + Number3 * 2^2 + Number4 * 2^3
    

    范围0-50你要存储4个数字0-49

    Number1 + Number2 * 50^1 + Number3 * 50^2 + Number4 * 50^3
    

    范围0-X你要存储N个数字0-X

    Number1 + Number2 * (X+1)^1 + Number3 * (X+1)^2 + ... + NumberN * (X+1)^(N-1)
    

    如果您的数字没有模式(因此它可以以某种方式被压缩),那么真的没有其他方法了。

    与素数不同,计算机解数也超级容易


    预定值

    @FlorainK 评论指出我错过的事实

    (我可以决定哪些数字)

    唯一合乎逻辑的解决方案是提供您的数字参考

    0 is 15342
    1 is 6547
    2 is 76234
    3 is "i like stack overflow"
    4 is 42141
    

    所以你将工作范围0-4(5 个选项)和任何组合长度。 “编码”和“解码”数字时使用参考

    一千个值?

    因此您将使用 0-999 范围

    0 is 62342
    1 is 7456345653
    2 is 45656234532
    ...
    998 is 7623452
    999 is 4324234326453
    

    假设您使用 64 位系统和支持 64 位整数的编程/数据库语言

    2^64 = 18446744073709551616
    

    您的最大范围是 1000^X < 18446744073709551616,其中 X 是您可以存储在一个 64 位整数中的数字数量

    只有 6 个。

    您只能存储 6 个单独的数字 0-999,它们适合一个 64 位整数。

    0,0,0,0,0,0 is 0
    1,0,0,0,0,0 is 1
    0,1,0,0,0,0 is 1000
    999,999,999,999,999,999 is ~1e+18
    

    【讨论】:

    • 问题表明他可以决定哪些值可以存储。假设他想知道存在的四个数字 2、6、12、49,那么他可以用 4 位编码该信息(连同从每个位到每个值的映射)。这比这个答案中提出的解决方案效率更高。
    • 我认为如果值可以多次出现,您的解决方案是最有效的,例如,如果我们必须存储 [76234, 87345, 76234]。但是,如果不能出现倍数,我认为 5 位更有效。 @valepu 给出的示例解决方案似乎表明不会出现重复值,因为他的解决方案不适合它们。
    • @FlorianK 我编辑了一些答案,阅读并思考。他想将多个 [0-999] 存储为一位。我没有考虑任何其他解决方案。
    • 在一个 64 位整数中,他可以存储 64 个预先确定的数字,无论它们是否在集合中(第一位表示第一个数字是否存在,第二位表示第二个数字等)。这是他在原始帖子中提到的位掩码解决方案,其中对于 3 个“数字”(在本例中为读取、写入和执行),无论它们是否存在,它都存储在 3 位中。
    • @Peter 是的,对不起,如果我不清楚,我的意思是我有“a,b,c,d e”然后我想存储,比如说在数据库中,“a b c ”连续,“a b c d”在另一个中,“a d e”在另一个中使用单个整数,我不希望这些整数增长太多,达到高于“最大整数”的值,如果我周围有东西1000 个值
    【解决方案3】:

    好的,所以你想存储“a,b,c”或“a,b”或“a,b,c,d”或“a”等(感谢@FlorianK)

    在这种情况下,可以使用位运算符和二的幂

    $a = 1 << 0; // 1
    $b = 1 << 1; // 2
    $c = 1 << 2; // 4
    $d = 1 << 3; // 8
    .. etc
    

    假设$flag$a$c

    $flag = $a | $c; // $flag is integer here
    

    现在检查一下

    $ok = ($flag & $a) && ($flag & $c); // true
    $ok = ($flag & $a) && ($flag & $b); // false
    

    所以在 64 位系统/语言/操作系统中,您最多可以使用 64 个标志,从而为您提供 2^64 种组合

    没有其他选择。素数在这方面更糟糕,因为您在中间跳过许多数字,而二进制系统使用每个数字。


    我看到您正在使用数据库,并且您想将其存储在数据库中。

    我真的认为我们在这里处理的是XY Problem,你应该重新考虑你的申请,而不是做出这样的变通办法。

    【讨论】:

    • 其实素数法浪费了相当多的数字。例如,所有 4 的倍数都会对 2 编码的权限在集合中出现两次的情况进行编码,这是不可能发生的。所以这已经是浪费数字的四分之一了,但同样的情况也适用于 9 (3 * 3) 的倍数等。
    • @FlorianK 你当然是对的。 4 是 2 x 2,8 是 2x2x2 等等。我说素数在原始答案中浪费了数字,然后我大脑冻结了,我编辑了我的答案
    猜你喜欢
    • 2018-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-21
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多