如何从较大(11 字节)的唯一编号生成较小的唯一编号?将军C
How to generate smaller unique number from larger (11 bytes) unique number? General C
我有好几台设备。它们每个都有由 11 个字节组成的唯一 ID,例如:0x34 0x57 0x36 0x31 0x38 0x39 0x15 0x0e 0x00 0x0f 0x00。我想生成唯一编号(0-32767 或 0-65535)以在广播 FindDevices 命令时延迟应答。当设备同时开始响应时,会出现 RS485 总线争用问题,所以我想避免这种情况。
我的第一次尝试是对唯一 ID 的所有 11 个字节求和,调用 srand(sum) 到种子生成器,然后调用 rand() 来获取我的值。
但不幸的是,这是一个糟糕的解决方案,在我的一批设备中,我有 2 台设备具有唯一 ID 但具有相同的“(不是那么)唯一”编号 :(
UID1: 34 57 36 31 38 39 15 0e 00 0f 00
总和:405 十进制
生成数:23860
UID2: 34 57 33 31 35 39 04 18 00 1c 00
总和:405 十进制
生成数:23860
设备不知道在其他设备中生成了什么数字或者它们有什么唯一 ID,所以它们不能简单地比较它们。
知道如何生成这样的唯一编号(0-32767 或 0-65535)吗?
编辑:
我工作台中批次的唯一 ID(十六进制)列表:
01. 34 57 36 31 38 39 15 0E 00 02 00
02. 34 57 36 31 38 39 15 0E 00 06 00
03. 34 57 36 31 38 39 15 0E 00 0A 00
04. 34 57 36 31 38 39 15 0E 00 0E 00
05. 34 57 36 31 38 39 15 0A 00 14 00
06. 34 57 36 31 38 39 15 0A 00 1C 00
07. 34 57 36 31 38 39 15 09 00 23 00
08. 34 57 36 31 38 39 15 0A 00 24 00
09. 34 57 36 31 38 39 0E 1D 00 1A 00
10. 34 57 36 31 38 39 15 0E 00 09 00
11. 34 57 33 31 35 39 04 10 00 20 00
12. 34 57 33 31 35 39 04 18 00 1C 00
13. 34 57 36 31 38 39 15 0E 00 0F 00
14. 34 57 36 31 38 39 15 0E 00 13 00
15. 34 57 36 31 38 39 15 0E 00 17 00
16. 34 57 36 31 38 39 15 0E 00 1F 00
17. 34 57 36 31 38 39 15 0A 00 25 00
看起来它们是唯一的,但很多字节是 repeated/constant。好的解决方案应该生成位于整个范围内的值,即使输入值彼此接近 :)
编辑2:
以下是您回答的所有解决方案的结果:
Test results:
OP: 1977, H1L: 14759, H1H: 13938, H2L: 7189, H2H: 36686, H3L: 14759, H3H: 13938, H4: 2652, PRS: 61086
OP: 3669, H1L: 13735, H1H: 12914, H2L: 8213, H2H: 37710, H3L: 13735, H3H: 12914, H4: 6748, PRS: 25852
OP: 5361, H1L: 16807, H1H: 15986, H2L: 5141, H2H: 34638, H3L: 16807, H3H: 15986, H4: 10844, PRS: 40974
OP: 7053, H1L: 15783, H1H: 14962, H2L: 6165, H2H: 35662, H3L: 15783, H3H: 14962, H4: 14940, PRS: 19836
OP: 7899, H1L: 18507, H1H: 25943, H2L: 3441, H2H: 24681, H3L: 18507, H3H: 25943, H4: 21076, PRS: 4898
OP: 11283, H1L: 20555, H1H: 27991, H2L: 1393, H2H: 22633, H3L: 20555, H3H: 27991, H4: 29268, PRS: 10065
OP: 13821, H1L: 391, H1H: 26260, H2L: 21557, H2H: 24364, H3L: 391, H3H: 26260, H4: 36434, PRS: 63904
OP: 14667, H1L: 30795, H1H: 38231, H2L: 23902, H2H: 12393, H3L: 30795, H3H: 38231, H4: 37460, PRS: 46300
OP: 15513, H1L: 23009, H1H: 40628, H2L: 31688, H2H: 9996, H3L: 23009, H3H: 40628, H4: 27066, PRS: 21678
OP: 21322, H1L: 17063, H1H: 16242, H2L: 4885, H2H: 34382, H3L: 17063, H3H: 16242, H4: 9820, PRS: 60787
OP: 22168, H1L: 31736, H1H: 54522, H2L: 22961, H2H: 61623, H3L: 31736, H3H: 54522, H4: 32801, PRS: 20737
OP: 23860, H1L: 3760, H1H: 10032, H2L: 18188, H2H: 40592, H3L: 3760, H3H: 10032, H4: 28721, PRS: 50696
OP: 23860, H1L: 15527, H1H: 14706, H2L: 6421, H2H: 35918, H3L: 15527, H3H: 14706, H4: 15964, PRS: 28319
OP: 25552, H1L: 10407, H1H: 9586, H2L: 11541, H2H: 41038, H3L: 10407, H3H: 9586, H4: 20060, PRS: 60097
OP: 27244, H1L: 9383, H1H: 8562, H2L: 12565, H2H: 42062, H3L: 9383, H3H: 8562, H4: 24156, PRS: 5512
OP: 30628, H1L: 11431, H1H: 10610, H2L: 10517, H2H: 40014, H3L: 11431, H3H: 10610, H4: 32348, PRS: 55107
OP: 31474, H1L: 30539, H1H: 37975, H2L: 24158, H2H: 12649, H3L: 30539, H3H: 37975, H4: 38484, PRS: 4379
OP: 20035, H1L: 0, H1H: 0, H2L: 21948, H2H: 50624, H3L: 0, H3H: 0, H4: 0, PRS: 26124
OP 是我的原始方法,它很差并且为列出的 UID 生成非唯一编号。
H1L 和 H1H 是 chux 提供的简化解决方案。
H2L, H2H, H3L, H3H 是我的修改(在下部或上部添加 ~ )以查看它是否产生更好的结果。
H4 是 alain 提出的解决方案。
PRS 是 Pearson 回归 uint16_t,由 mattinbits 建议。
获胜者是... PRS! :) 您的所有建议都从列出的 UID 生成唯一编号,因此它们是正确的,但 Pearson 散列 提供最好的结果方差(在 Excel ;) 中检查)。
感谢您的帮助!
您要做的是有效地为您的 ID 找到一个散列,其中散列输出小于输入,因此总会有冲突的风险,即两个设备 ID 产生相同的短 ID。尽管如此,与求和方法相比,使用适当的散列函数会更幸运。只需寻找一个简单的 8 位或 16 位哈希函数并使用它。例如。 https://en.m.wikipedia.org/wiki/Pearson_hashing
从较大的数字(11 字节或 88 位)生成 15 或 16 位范围内的 唯一数字 是 hash function 并且容易发生冲突,因为发生在 OP 生成的号码 23860 上。
11个字节的总和非常弱,因为11个字节的总和在0到11*255或2805的范围内,分布非常不均匀。所以代码只为 srand()
生成 2806 个不同的种子。使用比 8 位更宽的整数分组会更好。推荐 64 位组相互异或。
使用 srand() / rand()
是一种方法,但有缺点 一致性 :1) 可移植性:不同平台上的相同数据可能会产生不同的数字,因为 C 对 rand()
方法。 2) 因为它需要 2 个函数共享一个全局状态变量,所以代码必须确保另一个 thread/interrupt 不会把事情搞砸,或者这些调用会破坏使用 rand()
的其他函数的一致性。 rand()
的一个大问题是在 RAND_MAX
是 32767 的系统上,它的最小指定值,并且代码正在尝试 [0...65536].
我发现一致性很重要,因为能够在多个平台上使用相同的测试代码:代码维护方面的显着优势。
很好地推荐了一个很好的 8/16 位解决方案。为什么要重新发明轮子?但是我不使用战车轮胎,所以...
如果 OP 似乎并不真正需要 [0...32767] 或 [0...65536] 的 整个 范围内的标识符(OP 是否意味着 65535 ?),考虑一个简单的可移植可重复哈希方法,该方法依赖于 %
接近极限的 prime 来很好地混合位。
// return numbers 0 ... 32748 or 0 ... 65536
unsigned long Hash(const unsigned char ID[11]) {
unsigned long long Upper;
unsigned long Lower;
Upper = (ID[0]*1ULL<<56) | (ID[1]*1ULL<<48) | (ID[2]*1ULL<<40) | (ID[3]*1ULL<<32) |
(ID[4]*1UL<<24) | (ID[5]*1UL<<16) | (ID[6]*1U<<8) | ID[7];
Lower = (ID[8]*1UL<<16) | (ID[9]*1U<<8) | ID[10];
// Greatest prime <= 32768
#define Prime_LE_32768 32749
return (Upper ^ Lower) % Prime_LE_32768;
// or
// Greatest prime <= 65537
#define Prime_LE_65537 65537
return (Upper ^ Lower) % Prime_LE_65537;
}
[编辑] 可能进行了简化。
unsigned Hash(const uint8_t ID[11], unsigned prime) {
uint64_t H[2] = {0};
memcpy(H, ID, 11);
return (H[0] ^ H[1]) % prime;
}
const unsigned char ID[][11] = {
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x02, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x06, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x0A, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x0E, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x14, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x1C, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x09, 0x00, 0x23, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x24, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x0E, 0x1D, 0x00, 0x1A, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x09, 0x00 },
{ 0x34, 0x57, 0x33, 0x31, 0x35, 0x39, 0x04, 0x10, 0x00, 0x20, 0x00 },
{ 0x34, 0x57, 0x33, 0x31, 0x35, 0x39, 0x04, 0x18, 0x00, 0x1C, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x0F, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x13, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x17, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x1F, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x25, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
#define Prime_LE_32768 32749
#define Prime_LE_65536 65521u
void test() {
int i, j;
for (i = 0; i < sizeof ID / sizeof ID[0]; i++) {
const char *comma = "";
for (j = 0; j < 11; j++) {
printf("%s%02X", comma, ID[i][j]);
comma = "-";
}
printf(" %5u", Hash(ID[i], Prime_LE_32768));
printf(" %5u\n", Hash(ID[i], Prime_LE_65536));
}
puts("");
}
Output
34-57-36-31-38-39-15-0E-00-02-00 14759 13938
34-57-36-31-38-39-15-0E-00-06-00 13735 12914
34-57-36-31-38-39-15-0E-00-0A-00 16807 15986
34-57-36-31-38-39-15-0E-00-0E-00 15783 14962
34-57-36-31-38-39-15-0A-00-14-00 18507 25943
34-57-36-31-38-39-15-0A-00-1C-00 20555 27991
34-57-36-31-38-39-15-09-00-23-00 391 26260
34-57-36-31-38-39-15-0A-00-24-00 30795 38231
34-57-36-31-38-39-0E-1D-00-1A-00 23009 40628
34-57-36-31-38-39-15-0E-00-09-00 17063 16242
34-57-33-31-35-39-04-10-00-20-00 31736 54522
34-57-33-31-35-39-04-18-00-1C-00 3760 10032
34-57-36-31-38-39-15-0E-00-0F-00 15527 14706
34-57-36-31-38-39-15-0E-00-13-00 10407 9586
34-57-36-31-38-39-15-0E-00-17-00 9383 8562
34-57-36-31-38-39-15-0E-00-1F-00 11431 10610
34-57-36-31-38-39-15-0A-00-25-00 30539 37975
00-00-00-00-00-00-00-00-00-00-00 0 0
每个设备都自行生成 ID 的解决方案(与 "hash" 方法一样)的优点是无需通信即可工作。另一方面,数量必须足够大,这样碰撞就不太可能发生。 16位并不多。
我看到的一种实用方法是使用基于设备 属性 的数字,如处理器序列号或 RS485 总线地址(但我怀疑你想使用我们的 ID正是为了这个目的而谈论的)。
如果 11 字节的唯一 ID 或您有权访问的序列号不是随机的,您可以使用这种非随机性来制作比任何通用哈希函数都更好的哈希函数。
这是因为通用哈希函数必须均匀混合位,因为它不知道哪些位比其他位变化更大。如果您知道哪些位变化最大,则可以使用这些知识来构建更好的散列函数。例如,如果您有一个计数器,则最低有效位是为 "unique" ID 选择的位。
如果这不可能,我会尝试制定一个检测冲突 ID 的通信方案。这应该是可能的,因为每个设备都能看到 RS485 总线上的所有通信。
编辑:根据示例数据,我会 select 这些位:
00 00 00 00 01 00 1B 1F 00 3F 00
并实现一个哈希函数,如:
unsigned short hash(const unsigned char ID[11]) {
return (ID[9] << 10)
| ((ID[6] & 0x18) << 5)
| ((ID[6] & 0x03) << 6)
| ((ID[7] & 0x1F) << 1)
| (ID[4] & 0x01);
}
此功能很可能为将来添加的设备生成唯一标识符。
我有好几台设备。它们每个都有由 11 个字节组成的唯一 ID,例如:0x34 0x57 0x36 0x31 0x38 0x39 0x15 0x0e 0x00 0x0f 0x00。我想生成唯一编号(0-32767 或 0-65535)以在广播 FindDevices 命令时延迟应答。当设备同时开始响应时,会出现 RS485 总线争用问题,所以我想避免这种情况。
我的第一次尝试是对唯一 ID 的所有 11 个字节求和,调用 srand(sum) 到种子生成器,然后调用 rand() 来获取我的值。 但不幸的是,这是一个糟糕的解决方案,在我的一批设备中,我有 2 台设备具有唯一 ID 但具有相同的“(不是那么)唯一”编号 :(
UID1: 34 57 36 31 38 39 15 0e 00 0f 00 总和:405 十进制 生成数:23860
UID2: 34 57 33 31 35 39 04 18 00 1c 00 总和:405 十进制 生成数:23860
设备不知道在其他设备中生成了什么数字或者它们有什么唯一 ID,所以它们不能简单地比较它们。
知道如何生成这样的唯一编号(0-32767 或 0-65535)吗?
编辑:
我工作台中批次的唯一 ID(十六进制)列表:
01. 34 57 36 31 38 39 15 0E 00 02 00
02. 34 57 36 31 38 39 15 0E 00 06 00
03. 34 57 36 31 38 39 15 0E 00 0A 00
04. 34 57 36 31 38 39 15 0E 00 0E 00
05. 34 57 36 31 38 39 15 0A 00 14 00
06. 34 57 36 31 38 39 15 0A 00 1C 00
07. 34 57 36 31 38 39 15 09 00 23 00
08. 34 57 36 31 38 39 15 0A 00 24 00
09. 34 57 36 31 38 39 0E 1D 00 1A 00
10. 34 57 36 31 38 39 15 0E 00 09 00
11. 34 57 33 31 35 39 04 10 00 20 00
12. 34 57 33 31 35 39 04 18 00 1C 00
13. 34 57 36 31 38 39 15 0E 00 0F 00
14. 34 57 36 31 38 39 15 0E 00 13 00
15. 34 57 36 31 38 39 15 0E 00 17 00
16. 34 57 36 31 38 39 15 0E 00 1F 00
17. 34 57 36 31 38 39 15 0A 00 25 00
看起来它们是唯一的,但很多字节是 repeated/constant。好的解决方案应该生成位于整个范围内的值,即使输入值彼此接近 :)
编辑2: 以下是您回答的所有解决方案的结果:
Test results:
OP: 1977, H1L: 14759, H1H: 13938, H2L: 7189, H2H: 36686, H3L: 14759, H3H: 13938, H4: 2652, PRS: 61086
OP: 3669, H1L: 13735, H1H: 12914, H2L: 8213, H2H: 37710, H3L: 13735, H3H: 12914, H4: 6748, PRS: 25852
OP: 5361, H1L: 16807, H1H: 15986, H2L: 5141, H2H: 34638, H3L: 16807, H3H: 15986, H4: 10844, PRS: 40974
OP: 7053, H1L: 15783, H1H: 14962, H2L: 6165, H2H: 35662, H3L: 15783, H3H: 14962, H4: 14940, PRS: 19836
OP: 7899, H1L: 18507, H1H: 25943, H2L: 3441, H2H: 24681, H3L: 18507, H3H: 25943, H4: 21076, PRS: 4898
OP: 11283, H1L: 20555, H1H: 27991, H2L: 1393, H2H: 22633, H3L: 20555, H3H: 27991, H4: 29268, PRS: 10065
OP: 13821, H1L: 391, H1H: 26260, H2L: 21557, H2H: 24364, H3L: 391, H3H: 26260, H4: 36434, PRS: 63904
OP: 14667, H1L: 30795, H1H: 38231, H2L: 23902, H2H: 12393, H3L: 30795, H3H: 38231, H4: 37460, PRS: 46300
OP: 15513, H1L: 23009, H1H: 40628, H2L: 31688, H2H: 9996, H3L: 23009, H3H: 40628, H4: 27066, PRS: 21678
OP: 21322, H1L: 17063, H1H: 16242, H2L: 4885, H2H: 34382, H3L: 17063, H3H: 16242, H4: 9820, PRS: 60787
OP: 22168, H1L: 31736, H1H: 54522, H2L: 22961, H2H: 61623, H3L: 31736, H3H: 54522, H4: 32801, PRS: 20737
OP: 23860, H1L: 3760, H1H: 10032, H2L: 18188, H2H: 40592, H3L: 3760, H3H: 10032, H4: 28721, PRS: 50696
OP: 23860, H1L: 15527, H1H: 14706, H2L: 6421, H2H: 35918, H3L: 15527, H3H: 14706, H4: 15964, PRS: 28319
OP: 25552, H1L: 10407, H1H: 9586, H2L: 11541, H2H: 41038, H3L: 10407, H3H: 9586, H4: 20060, PRS: 60097
OP: 27244, H1L: 9383, H1H: 8562, H2L: 12565, H2H: 42062, H3L: 9383, H3H: 8562, H4: 24156, PRS: 5512
OP: 30628, H1L: 11431, H1H: 10610, H2L: 10517, H2H: 40014, H3L: 11431, H3H: 10610, H4: 32348, PRS: 55107
OP: 31474, H1L: 30539, H1H: 37975, H2L: 24158, H2H: 12649, H3L: 30539, H3H: 37975, H4: 38484, PRS: 4379
OP: 20035, H1L: 0, H1H: 0, H2L: 21948, H2H: 50624, H3L: 0, H3H: 0, H4: 0, PRS: 26124
OP 是我的原始方法,它很差并且为列出的 UID 生成非唯一编号。 H1L 和 H1H 是 chux 提供的简化解决方案。 H2L, H2H, H3L, H3H 是我的修改(在下部或上部添加 ~ )以查看它是否产生更好的结果。 H4 是 alain 提出的解决方案。 PRS 是 Pearson 回归 uint16_t,由 mattinbits 建议。
获胜者是... PRS! :) 您的所有建议都从列出的 UID 生成唯一编号,因此它们是正确的,但 Pearson 散列 提供最好的结果方差(在 Excel ;) 中检查)。 感谢您的帮助!
您要做的是有效地为您的 ID 找到一个散列,其中散列输出小于输入,因此总会有冲突的风险,即两个设备 ID 产生相同的短 ID。尽管如此,与求和方法相比,使用适当的散列函数会更幸运。只需寻找一个简单的 8 位或 16 位哈希函数并使用它。例如。 https://en.m.wikipedia.org/wiki/Pearson_hashing
从较大的数字(11 字节或 88 位)生成 15 或 16 位范围内的 唯一数字 是 hash function 并且容易发生冲突,因为发生在 OP 生成的号码 23860 上。
11个字节的总和非常弱,因为11个字节的总和在0到11*255或2805的范围内,分布非常不均匀。所以代码只为 srand()
生成 2806 个不同的种子。使用比 8 位更宽的整数分组会更好。推荐 64 位组相互异或。
使用 srand() / rand()
是一种方法,但有缺点 一致性 :1) 可移植性:不同平台上的相同数据可能会产生不同的数字,因为 C 对 rand()
方法。 2) 因为它需要 2 个函数共享一个全局状态变量,所以代码必须确保另一个 thread/interrupt 不会把事情搞砸,或者这些调用会破坏使用 rand()
的其他函数的一致性。 rand()
的一个大问题是在 RAND_MAX
是 32767 的系统上,它的最小指定值,并且代码正在尝试 [0...65536].
我发现一致性很重要,因为能够在多个平台上使用相同的测试代码:代码维护方面的显着优势。
如果 OP 似乎并不真正需要 [0...32767] 或 [0...65536] 的 整个 范围内的标识符(OP 是否意味着 65535 ?),考虑一个简单的可移植可重复哈希方法,该方法依赖于 %
接近极限的 prime 来很好地混合位。
// return numbers 0 ... 32748 or 0 ... 65536
unsigned long Hash(const unsigned char ID[11]) {
unsigned long long Upper;
unsigned long Lower;
Upper = (ID[0]*1ULL<<56) | (ID[1]*1ULL<<48) | (ID[2]*1ULL<<40) | (ID[3]*1ULL<<32) |
(ID[4]*1UL<<24) | (ID[5]*1UL<<16) | (ID[6]*1U<<8) | ID[7];
Lower = (ID[8]*1UL<<16) | (ID[9]*1U<<8) | ID[10];
// Greatest prime <= 32768
#define Prime_LE_32768 32749
return (Upper ^ Lower) % Prime_LE_32768;
// or
// Greatest prime <= 65537
#define Prime_LE_65537 65537
return (Upper ^ Lower) % Prime_LE_65537;
}
[编辑] 可能进行了简化。
unsigned Hash(const uint8_t ID[11], unsigned prime) {
uint64_t H[2] = {0};
memcpy(H, ID, 11);
return (H[0] ^ H[1]) % prime;
}
const unsigned char ID[][11] = {
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x02, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x06, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x0A, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x0E, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x14, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x1C, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x09, 0x00, 0x23, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x24, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x0E, 0x1D, 0x00, 0x1A, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x09, 0x00 },
{ 0x34, 0x57, 0x33, 0x31, 0x35, 0x39, 0x04, 0x10, 0x00, 0x20, 0x00 },
{ 0x34, 0x57, 0x33, 0x31, 0x35, 0x39, 0x04, 0x18, 0x00, 0x1C, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x0F, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x13, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x17, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0E, 0x00, 0x1F, 0x00 },
{ 0x34, 0x57, 0x36, 0x31, 0x38, 0x39, 0x15, 0x0A, 0x00, 0x25, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
#define Prime_LE_32768 32749
#define Prime_LE_65536 65521u
void test() {
int i, j;
for (i = 0; i < sizeof ID / sizeof ID[0]; i++) {
const char *comma = "";
for (j = 0; j < 11; j++) {
printf("%s%02X", comma, ID[i][j]);
comma = "-";
}
printf(" %5u", Hash(ID[i], Prime_LE_32768));
printf(" %5u\n", Hash(ID[i], Prime_LE_65536));
}
puts("");
}
Output
34-57-36-31-38-39-15-0E-00-02-00 14759 13938
34-57-36-31-38-39-15-0E-00-06-00 13735 12914
34-57-36-31-38-39-15-0E-00-0A-00 16807 15986
34-57-36-31-38-39-15-0E-00-0E-00 15783 14962
34-57-36-31-38-39-15-0A-00-14-00 18507 25943
34-57-36-31-38-39-15-0A-00-1C-00 20555 27991
34-57-36-31-38-39-15-09-00-23-00 391 26260
34-57-36-31-38-39-15-0A-00-24-00 30795 38231
34-57-36-31-38-39-0E-1D-00-1A-00 23009 40628
34-57-36-31-38-39-15-0E-00-09-00 17063 16242
34-57-33-31-35-39-04-10-00-20-00 31736 54522
34-57-33-31-35-39-04-18-00-1C-00 3760 10032
34-57-36-31-38-39-15-0E-00-0F-00 15527 14706
34-57-36-31-38-39-15-0E-00-13-00 10407 9586
34-57-36-31-38-39-15-0E-00-17-00 9383 8562
34-57-36-31-38-39-15-0E-00-1F-00 11431 10610
34-57-36-31-38-39-15-0A-00-25-00 30539 37975
00-00-00-00-00-00-00-00-00-00-00 0 0
每个设备都自行生成 ID 的解决方案(与 "hash" 方法一样)的优点是无需通信即可工作。另一方面,数量必须足够大,这样碰撞就不太可能发生。 16位并不多。
我看到的一种实用方法是使用基于设备 属性 的数字,如处理器序列号或 RS485 总线地址(但我怀疑你想使用我们的 ID正是为了这个目的而谈论的)。
如果 11 字节的唯一 ID 或您有权访问的序列号不是随机的,您可以使用这种非随机性来制作比任何通用哈希函数都更好的哈希函数。
这是因为通用哈希函数必须均匀混合位,因为它不知道哪些位比其他位变化更大。如果您知道哪些位变化最大,则可以使用这些知识来构建更好的散列函数。例如,如果您有一个计数器,则最低有效位是为 "unique" ID 选择的位。
如果这不可能,我会尝试制定一个检测冲突 ID 的通信方案。这应该是可能的,因为每个设备都能看到 RS485 总线上的所有通信。
编辑:根据示例数据,我会 select 这些位:
00 00 00 00 01 00 1B 1F 00 3F 00
并实现一个哈希函数,如:
unsigned short hash(const unsigned char ID[11]) {
return (ID[9] << 10)
| ((ID[6] & 0x18) << 5)
| ((ID[6] & 0x03) << 6)
| ((ID[7] & 0x1F) << 1)
| (ID[4] & 0x01);
}
此功能很可能为将来添加的设备生成唯一标识符。