基数排序基数 16(十六进制)
Posted
技术标签:
【中文标题】基数排序基数 16(十六进制)【英文标题】:Radix Sort Base 16 (Hexadecimals) 【发布时间】:2017-03-14 00:45:51 【问题描述】:我花了 10 小时以上的时间尝试在 LSD 基数排序中对以下(十六进制)进行排序,但无济于事。网上关于这个主题的资料很少。
0 4c7f cd80 41fc 782c 8b74 7eb1 9a03 aa01 73f1
我知道我必须屏蔽并执行按位运算来处理每个十六进制数字(4 位),但不知道如何以及在哪里。
我正在使用来自GeeksforGeeks的代码(我理解)
void rsort(int a[], int n)
int max = getMax(a, n);
for (int exp = 1; max / exp > 0; exp *= 10)
ccsort(a, n, exp);
int getMax(int a[], int n)
int max = a[0];
int i = 0;
for (i = 0; i < n; i++)
if (a[i] > max)
max = a[i];
return max;
void ccsort(int a[], int n, int exp)
int count[n];
int output[n];
int i = 0;
for (i = 0; i < n; i++)
count[i] = 0;
output[i] = 0;
for (i = 0; i < n; i++)
++count[(a[i] / exp) % 10];
for (i = 1; i <= n; i++)
count[i] += count[i - 1];
for (i = n - 1; i >= 0; i--)
output[count[(a[i] / exp) % 10] - 1] = a[i];
--count[(a[i] / exp) % 10];
for (i = 0; i < n; i++)
a[i] = output[i];
我也检查了所有关于这个问题的 ***,但没有一个包括细节。
【问题讨论】:
变量exp
未正确使用。见this article for an example。您需要向下滚动到标题为“C 中的示例” 的部分。请注意,它们的 exp
从 1 开始,并在每次通过循环时乘以基数。
@WeatherVane,不是文本,它们是数组的一部分,比如主函数中的数组。
【参考方案1】:
您的基数排序实现有点不正确:
它不能处理负数 函数ccsort()
中的数组count[]
的大小应为10
而不是n
。如果n
小于10
,则该函数不起作用。
用于累积计数的循环走得太远了:for (i = 1; i <= n; i++)
。 <=
运算符再次导致错误。
您说您按十六进制数字排序,但代码使用十进制数字。
这是一个(略微)改进的版本,带有解释:
void ccsort(int a[], int n, int exp)
int count[10] = 0 ;
int output[n];
int i, last;
for (i = 0; i < n; i++)
// compute the number of entries with any given digit at level exp
++count[(a[i] / exp) % 10];
for (i = last = 0; i < 10; i++)
// update the counts to have the index of the place to dispatch the next
// number with a given digit at level exp
last += count[i];
count[i] = last - count[i];
for (i = 0; i < n; i++)
// dispatch entries at the right index for its digit at level exp
output[count[(a[i] / exp) % 10]++] = a[i];
for (i = 0; i < n; i++)
// copy entries batch to original array
a[i] = output[i];
int getMax(int a[], int n)
// find the largest number in the array
int max = a[0];
for (int i = 1; i < n; i++)
if (a[i] > max)
max = a[i];
return max;
void rsort(int a[], int n)
int max = getMax(a, n);
// for all digits required to express the maximum value
for (int exp = 1; max / exp > 0; exp *= 10)
// sort the array on one digit at a time
ccsort(a, n, exp);
由于所有的除法和模运算,上述版本效率很低。可以使用移位和掩码来执行十六进制数字:
void ccsort16(int a[], int n, int shift)
int count[16] = 0 ;
int output[n];
int i, last;
for (i = 0; i < n; i++)
++count[(a[i] >> shift) & 15];
for (i = last = 0; i < 16; i++)
last += count[i];
count[i] = last - count[i];
for (i = 0; i < n; i++)
output[count[(a[i] >> shift) & 15]++] = a[i];
for (i = 0; i < n; i++)
a[i] = output[i];
void rsort16(int a[], int n)
int max = a[0];
for (int i = 1; i < n; i++)
if (a[i] > max)
max = a[i];
for (int shift = 0; (max >> shift) > 0; shift += 4)
ccsort16(a, n, shift);
使用包含 256 个条目的 count
数组一次排序一个字节的速度大约是两倍。如 rcgldr 的回答所示,一次计算所有数字的计数也会更快。
请注意,此实现仍然无法处理负数。
【讨论】:
我明白你的观点。我认为在使用循环、标志和交换等方式对列表进行排序后,负数很容易排序。 wb 无符号浮点数? @itproxti:如果您希望对有符号整数进行排序,您可以通过添加INT_MIN
的偏移量来修改上述代码,并且基数排序将正确处理负数。使用当前代码,负数按递增值排序,但在数组末尾分组。可以通过循环将它们移到开头,但在原地执行此操作很棘手。关于浮点值,基数排序算法有时可以使用,但不能移植,因为浮点的内部表示因一个系统而异,可能不适合基数排序。【参考方案2】:
有一种更简单的方法来实现基数排序。检查最大值后,找到 16 >= 最大值的最低幂。这可以通过循环中的 max >>= 4 来完成,增加 x 以便当 max 变为零时,x 的 16 次幂 >= 原始最大值。例如,最大值 0xffff 需要 4 次基数排序,而最大值 0xffffffff 需要 8 次基数排序。
如果值的范围最有可能采用整数可用的完整范围,则无需费心确定最大值,只需根据整数大小进行基数排序。
您的示例代码显示了一个基数排序,由于计数转换为索引的方式,它向后扫描数组。这可以通过使用另一种方法将计数转换为索引来避免。以下是 32 位无符号整数的基数 256 基数排序示例。它使用计数/索引矩阵,因此所有 4 行计数都是通过数组的一次读取传递生成的,然后是 4 次基数排序传递(因此排序后的数据最终回到原始数组中)。 std::swap 是一个交换指针的 C++ 函数,对于 C 程序,这可以通过内联交换指针来替换。 t = 一个; a = b; b = t,其中 t 的类型为 uint32_t *(ptr 为无符号 32 位整数)。对于基数为 16 的基数排序,矩阵大小为 [8][16]。
// a is input array, b is working array
uint32_t * RadixSort(uint32_t * a, uint32_t *b, size_t count)
size_t mIndex[4][256] = 0; // count / index matrix
size_t i,j,m,n;
uint32_t u;
for(i = 0; i < count; i++) // generate histograms
u = a[i];
for(j = 0; j < 4; j++)
mIndex[j][(size_t)(u & 0xff)]++;
u >>= 8;
for(j = 0; j < 4; j++) // convert to indices
m = 0;
for(i = 0; i < 256; i++)
n = mIndex[j][i];
mIndex[j][i] = m;
m += n;
for(j = 0; j < 4; j++) // radix sort
for(i = 0; i < count; i++) // sort by current lsb
u = a[i];
m = (size_t)(u>>(j<<3))&0xff;
b[mIndex[j][m]++] = u;
std::swap(a, b); // swap ptrs
return(a);
【讨论】:
所以,如果我理解正确的话。你是说不是一次排序整数(4个字节),而是一次排序1个字节,作为回报会加速算法?我仍然看不到它。谢谢! @itproxti - 基数排序是对整数进行排序,一次 4 个字节,但基数排序的每次传递都基于每个整数中的一个字节字段。第一遍根据最低有效字节对整数进行排序,第四遍和最后一遍根据最高有效字节对整数进行排序。因此,总共是一次读取传递来创建计数/索引矩阵,然后是四次基数排序传递来对数据进行排序。相比之下,原始问题示例需要传递 9 或 10 次才能对 32 位整数进行排序,因为它每次通过一位小数位进行排序。【参考方案3】:void int_radix_sort(void)
int group; //because extracting 8 bits
int buckets = 1 << 8; //using size 256
int map[buckets];
int mask = buckets - 1;
int i;
int cnt[buckets];
int flag = NULL;
int partition;
int *src, *dst;
for (group = 0; group < 32; group += 8)
// group = 8, number of bits we want per round, we want 4 rounds
// cnt
for (int i = 0; i < buckets; i++)
cnt[i] = 0;
for (int j = 0; j < n; j++)
i = (lst[j] >> group) & mask;
cnt[i]++;
tmp[j] = lst[j];
//map
map[0] = 0;
for (int i = 1; i < buckets; i++)
map[i] = map[i - 1] + cnt[i - 1];
//move
for (int j = 0; j < n; j++)
i = (tmp[j] >> group) & mask;
lst[map[i]] = tmp[j];
map[i]++;
经过数小时的研究,我找到了答案。我仍然不明白此代码/答案中发生了什么。我无法理解这个概念。希望有人能解释一下。
【讨论】:
【参考方案4】:我明白你的观点。我认为在使用循环、标志和交换等方式对列表进行排序后,负数很容易排序。 wb 无符号浮点数? – itproxti 2016 年 11 月 1 日 16:02
至于处理浮点数可能有办法,例如345.768是数字,需要转换成整数,即345768,我乘以1000。就像偏移量将 -ve 数字移动到 +ve 域一样,乘以 1000、10000 等会将浮点数转换为小数部分全为零的数字。然后可以将它们类型转换为 int 或 long。但是对于较大的值,整个重整后的数字可能无法容纳在整个 int 或 long 范围内。
要相乘的数字必须是常数,就像偏移量一样,这样才能保留大小之间的关系。最好使用 2 的幂,例如 8 或 16,因为可以使用位移运算符。然而,就像计算偏移量需要一些时间一样,计算乘数也需要一些时间。将搜索整个数组以计算最小的数字,当乘以时将所有数字在小数部分为零。
这可能计算速度不快,但如果需要,仍然可以完成这项工作。
【讨论】:
以上是关于基数排序基数 16(十六进制)的主要内容,如果未能解决你的问题,请参考以下文章