输入: 我有一些数组,如:
1, 2, 3, 4, 5
2, 1, 3, 4, 5
3, 2, 5, 4, 1
5, 4, 3, 1, 2
.....
所有这些都是5位数的非重复排列 - 5C5。行可以重复,但行中的任何数字都是唯一的。
目的: 计算输入数据中每种类型(排列)的数组数量。
我的想法:
5C5表示只有120个唯一行。所以我可以在int[120]
数组中存储计数器。并在阅读输入时递增它们。
我的问题: 是否有任何有效的算法将此数组转换(哈希)到数组索引?
优先语言是C,带有指针和手动内存管理。在完美的情况下,我尝试做类似的事情:
FILE *f;
int counters[120] = {0};
char seq[20];
parse_line(f, seq); #scans and parses string into array
counters[hash(seq)]++;
PS: 通过解决" UVa 157 - Recycling",我对这个问题的启发。后来我看到了解决方案并理解我误解了任务,但问题没有得到解答。
答案 0 :(得分:5)
进行基本转换。第一个数字位于基数5,第二个数字位于基数4,然后是基数3和基数2.因此,例如:
1, 2, 3, 4, 5 -> 0 * 4*3*2*1 + 0 * 3*2*1 + 0 * 2*1 + 0 * 1 -> 0
2, 1, 3, 4, 5 -> 1 * 4*3*2*1 + 0 * 3*2*1 + 0 * 2*1 + 0 * 1 -> 24
3, 2, 5, 4, 1 -> 2 * 4*3*2*1 + 1 * 3*2*1 + 2 * 2*1 + 1 * 1 -> 59
5, 4, 3, 1, 2 -> 4 * 4*3*2*1 + 3 * 3*2*1 + 2 * 2*1 + 0 * 1 -> 118
5, 4, 3, 2, 1 -> 4 * 4*3*2*1 + 3 * 3*2*1 + 2 * 2*1 + 1 * 1 -> 119
请记住,只计算您在选择数字时未见过的数字!小心翼翼地走过上面的第三排:
3, 2, 5, 4, 1
首先,我们将数字映射到数字:
1 2 3 4 5
0 1 2 3 4
由于第一个数字为3
,因此第一个数字为2
。现在我们从数字中删除3
,给出
1 2 4 5
0 1 2 3
下一个数字是2
,因此下一个数字是1
。映射现在是
1 4 5
0 1 2
下一个数字是5
,因此下一个数字是2
。映射现在是
1 4
0 1
下一个数字是4
,因此下一个数字是1
。最后一位数字将为0
,但它不会对总和作出任何贡献 - 最后一位数字是一元数字,因此它始终为0
。因此,数字32541
对应于数字21210
。
要计算基数为10的这个数字的值,我们使用通常的基本转换例程:我们将"列值乘以"通过当前列的基数,然后添加当前数字的值乘以列值。所以:
0 * 1
+ 1 * (1*1)
+ 2 * (2*1*1)
+ 1 * (3*2*1*1)
+ 2 * (4*3*2*1*1)
-----------------
59
另请参阅factorial number systems上的维基百科页面。
答案 1 :(得分:1)
最简单但内存消耗的解决方案是创建非冲突哈希。将数组转换为数字,假设排列仅包含5位数。数字的最大值只能是54321.取A[54321]
,从数字和增量计数器计算数字。
理论上,最佳无碰撞散列具有以下表达式:
如果S = s 0 s 1 s 2 ... s n-1
散列(S)= s 0 * M 0 + s 1 * M 1 + s 2 * M 3 ... s n-1 * M n-1
其中M是s i 可以采用的数字集的大小。
在你的情况下,M是5,n是5,
因此,哈希的最大值需要为
1 * 5 0 + 2 * 5 1 + 3 * 5 2 + 4 * 5 3 + 5 * 5 4 = 3711。