是否可以减少 DateTime.Now.Ticks.ToString("X") 的长度并仍然保持唯一性?

Posted

技术标签:

【中文标题】是否可以减少 DateTime.Now.Ticks.ToString("X") 的长度并仍然保持唯一性?【英文标题】:Is it possible to reduce the length of DateTime.Now.Ticks.ToString("X") and still maintain uniqueness? 【发布时间】:2017-01-08 18:17:00 【问题描述】:

我正在使用的某些硬件存在限制,其中我只能广播(无线)26 个字符。

为了克服这个限制,第一个广播传输一个转换为十六进制 (DateTime.Now.Ticks.ToString( "X" )) 的时间戳,以及正在传输的消息的长度(也作为十六进制字符串)。

接收软件测试头信息,当它确认收到一个时,将时间戳(重新转换为long)存储在字典中:

/*************************************************************************
 * _pendingMessages.Add( DateTime.Now.Ticks, Tuple.Create( MessageLength, string.Empty ) );
 * T.Item1 = Message Length
 * T.Item2 = Message ( when Message.Length == Length, Pop Message )
 *************************************************************************/
private static Dictionary<long, Tuple<long, string>> _pendingMessages;

不幸的是,每次都必须通过时间戳,而且它...超过了分配的字符长度的一半(目前为 15 个字符)。

所以我在想,与其传递整个时间戳,不如通过对十六进制字符串的字符值求和来减少它:

例如:

DateTime.Now.Ticks.ToSTring("X").Sum( C => C ).ToString("X");

不幸的是,一个快速的测试毫不客气地打破了这个想法

(复制键相当快):

Dictionary<string, long> _dctTest = new Dictionary<string, long>( );
while ( true )
    long dtNow = DateTime.Now.Ticks;
    string strKey = dtNow.ToString("X").Sum( C => C ).ToStrings("X");
    _dctTest.Add( strKey, dtNow ); //<=====Explodes after less than a second.

所以我的问题是 - 有什么方法可以可靠地减少我的“密钥”的长度,同时仍然(合理地)保证唯一性?

【问题讨论】:

在这里查看“G”:***.com/questions/15072552/…。您还可以存储一个哈希 ***.com/questions/3404715/… 将每个数字视为 int 数组的成员,例如 1234 变为 int[]1,2,3,4; 您实际需要什么精度?蜱也不能保证是唯一的,除非你做某事like this。 @Cᴏʀʏ 是的;我在看的时候看到了那个帖子。消息传输之间有合理的延迟,所以它不像我在发射航天飞机或其他毫秒意味着你最终偏离航向一万亿公里之类的东西。 @ShannonHolsinger 不熟悉“G”...可能值得研究。 @Will:你可以通过减去一个已知值来减少几位数字,比如直到本世纪初的滴答数,然后再对其进行任何操作。另一方面,加回已知的刻度数。 【参考方案1】:

这里有一些东西可以启动一些答案。我并不是说这是一个最佳解决方案,但我可以只用 11 个字符 8 个字符 7 个字符 的编码数据为您提供毫秒精度。

假设毫秒精度足够好,我们可以从一开始就降低算法的精度。一个刻度代表 100 纳秒。一毫秒内有 10,000 个滴答声。算法如下:

从过去发生的已知大量滴答开始。此示例使用本世纪初。

long centuryBegin = new DateTime(2001, 1, 1).Ticks; 
// 631139040000000000

现在拍摄当前时间戳的快照:

long currentDate = DateTime.Now.Ticks;
// 636083231371736598

取差,将精度降到毫秒级:

long shortTicks = (currentDate - centuryBegin) / 10000L; 
// 494419137173

现在我们只需对字符串进行 base64 编码:

string base64Ticks = Convert.ToBase64String(BitConverter.GetBytes(shortTicks));
// lVKtHXMAAAA=

但是,无需详细说明原因,尾随的“AAAA=”将出现在此字节数的任何编码数上,因此我们可以将其删除!

base64Ticks = base64Ticks.Substring(0, 7);
// lVKtHXM

您现在有一个 7 字符的字符串 lVKtHXM 用于传输。另一方面:

// Decode the base64-encoded string back into bytes
// Note we need to add on the "AAAA=" that we stripped off    
byte[] data = new byte[8];
Convert.FromBase64String(base64Ticks + "AAAA=").CopyTo(data, 0);

// From the bytes, convert back to a long, multiply by 10,000, and then
// add on the known century begin number of ticks
long originalTicks = (BitConverter.ToInt64(data, 0) * 10000L) + centuryBegin;
// 636083231371730000

我们来看看两者的区别:

 636083231371736598 (original ticks)
-636083231371730000 (decoded ticks)
===================
               6598 (difference)

您可以看到,这可以让您在原始时间戳的 6,598 个滴答声或 0.6598 毫秒内到达。差异总是

就唯一性而言,我在 100,000 次虚假传输上进行了尝试,每次尝试之间睡眠 1 毫秒,并且没有没有冲突

为了完善这篇文章,您可以使用以下一些辅助方法:

public static string EncodeTransmissionTimestamp(DateTime date)

    long shortTicks = (date.Ticks - 631139040000000000L) / 10000L; 
    return Convert.ToBase64String(BitConverter.GetBytes(shortTicks)).Substring(0, 7);


public static DateTime DecodeTransmissionTimestamp(string encodedTimestamp)

    byte[] data = new byte[8];
    Convert.FromBase64String(encodedTimestamp + "AAAA=").CopyTo(data, 0);
    return new DateTime((BitConverter.ToInt64(data, 0) * 10000L) + 631139040000000000L);

部分工作受到这篇文章的启发:Compress large Integers into smallest possible string

【讨论】:

这解释了你是多么有才华的开发者@Cory。你太棒了。感谢你的精彩解释。感谢你抽出宝贵的时间为世界做好事。

以上是关于是否可以减少 DateTime.Now.Ticks.ToString("X") 的长度并仍然保持唯一性?的主要内容,如果未能解决你的问题,请参考以下文章

我可以获得与 DateTime.Now.Ticks 等效的 VBA 吗?

使用 DateTime.Now.Ticks 生成唯一的数字 ID

请问DateTime.Now.Ticks.ToString()是啥意思

C# 怎样把 DateTime.Now.Ticks转换为常规日期的形式

DataTime.Now.Ticks的应用

JWT 验证失败