用断路器作为单个数值表示前十名单的方法

时间:2010-12-18 01:20:55

标签: c bit-shift

我有一个前十名的成绩(得分最高)和时间戳。

时间戳用于平局分数,在这种情况下,带有最低时间戳的并列分数获胜(第一个获得分数的人更高)。

排序数据集示例:

20 102906755
15 102910755
14 102890755
14 102890756
13 102890756

注意得分14上的平局,时间戳较小的那个位置较高。

我需要将得分和时间戳正确排序为单个32位值。

假设最高分为100万。

我通过减去第一个有效日期分数来减少时间戳值。

如何在C中实现这一目标?

4 个答案:

答案 0 :(得分:2)

首先,如果有超过2 ^ 32个不同的得分组合,你需要表示时间戳,那么游戏就结束了 - 无法完成。

考虑到这一点:

  • 你真的需要多少比分?如果最高得分是100万,那么你需要20比特,这使得均匀分布只留下12个时间戳。这将是非常紧张的,尤其如果可以在单个分数上绑定超过2 ^ 12 = 4000个列表条目,尽管分数分布可能不是甚至。
  • 您真正需要多少位时间戳?
  • 你可以丢弃时间戳中最重要的位吗?例如,如果您知道所有时间都将在2001之后,那么您可以将时间戳基于2001而不是1970,这将使您获得1位。 [编辑:你似乎已经改变了时间戳,它们不再像1970年那样看起来像他们原来那样的秒数]
  • 你可以从时间戳中丢弃不太重要的位吗?在您的示例数据中,您的时间戳只相差1秒,但这是否真实?如果你有两行得分和时间戳相等怎么办?据推测,这不是世界末日:如果1s的差距是可能的,那么我认为0s的差距是可能的。然后,例如,如果你将所有时间戳四舍五入到最接近的32位,那么你可以获得5位,代价是引入更多死关系。
  • 对于时间戳,您可以使用每次得分时增加一次的值,而不是自纪元以来以秒为单位的实际时间吗?如果是这样,那么你可以节省很多比特。你能为每个可能的分数使用不同的递增值,将你的例子数据转换成(20,0),(15,0),(14,0),(14,1),(13,0)?
  • 你能使用> 32位值吗?

如果其中任何一个的答案都很好,那么也许这是可能的。如果答案都很糟糕,那就根本不可能做你想做的事。

[编辑:考虑到以下评论,您的新问题的答案是:

double value = (double) score - ((double)timestamp) / (((long long)1) << 33);

更容易。直到2242年才好。

这假设double在您的实现中是64位,几乎是通用的。]

答案 1 :(得分:1)

假设你要使用无符号值,你的最高分为31。

您可以使用前5位作为分数,将较低的27位作为时间戳。

请注意,这会限制两个值的限制,因此您必须考虑可能的值以及尝试使用该范围之外的值时将执行的操作。

存储......

composite = ~(score << 27) | timestamp;

以后获取值:

#define TIMESTAMP_BITS ((1 << 27) - 1)
score = composite >> 27;
timestamp = composite & TIMESTAMP_BITS;

请注意,虽然您想在使用它们之前检查分数和时间戳,并且可能应用遮罩以确保值不重叠。

修改

Riderchap提出了一个很好的观点。作为示例,您提供的时间戳太大,无法与32位整数一起使用。我的答案更多的是原则上如何做到这一点的示范。

答案 2 :(得分:1)

虽然您经常说您实际上有double可用于存储信息,但我认为我仍然可能会分享一些使用32位整数执行此操作的想法。

首先,为了让这些数字按照得分顺序排序,时间顺序排在第二位,您希望得分占据较高值的位置,时间戳占据较低值。要将得分放在较高值的位置,我们必须选择乘以某个常数因子。可以用无符号32位整数表示的最大数字是4,294,967,295,我们的得分范围是0到1,000,000。这给了我们4,294的乘数。

然后我们有时间戳占用的低值位置 - 只需要添加它。这给了我们:

N = SCORE * 4294 + TIMESTAMP;

反向转换为:

SCORE = N / 4294;
TIMESTAMP = N % 4294;

但请注意,TIMESTAMP允许的范围为0到4293(含)。任何更高的内容都会溢出N所占据的SCORE部分。如果TIMESTAMP只是几秒钟(从最早的分数开始于4293并且倒计时),这只会给我们从最早的分数到最新分数的最长时间超过71分钟 - 可能不够。

这只是对可以使用32位整数表示的不同存储桶数量的限制 - 为了能够使用此模型表示更多不同的时间戳,您需要减少可以表示的不同分数。

但是,请注意,我们并不是真的希望将时间戳作为时间戳 - 我们只是希望它作为分数的单调排序。作为替代方案,我们可以保留一个柜台。将计数器初始化为4293.当新分数进入时,将其与计数器的当前值一起存储为其“时间戳”,然后递减计数器。这将允许在计数器用完之前存储4294个不同的高分。

作为进一步的改进,我们可以注意到,我们只关心相同SCORE值中的排序。这表明另一种选择:当新的高分进入时,找到该分数的当前最低TIMESTAMP值。将其减1,并将其用于新分数的“时间戳”(如果这是第一次提交此SCORE,请使用4293作为时间戳)。这将使每个人得分值达到4294分。

答案 3 :(得分:0)

根据高分,您需要切换要移位的位数。假设最大分数为255,这给我们一个8位的移位。

unsigned int combined = ~(score << 32-8) | (time & 0x0FFF);

这可能会缩短MSB的时间,但除非你期望与几年的差异打成平手,否则你会没事的。它将分数反转并将其置于前8位,然后将其与较低的24中的时间相结合。最小值将是最高分(倒置的高分=最低分和最低时间戳)。