在算法的上下文中啥构成“数组访问”?
Posted
技术标签:
【中文标题】在算法的上下文中啥构成“数组访问”?【英文标题】:What constitutes 'array access' in the context of algorithms?在算法的上下文中什么构成“数组访问”? 【发布时间】:2011-10-27 22:32:09 【问题描述】:下面是来自教科书的 Java 中的 LSD 基数排序实现,用于对字符串数组进行排序,其中每个字符串恰好包含 W
字符。
我想计算运行时数组访问的次数。我读过 LSD 排序应该需要n * c
数组访问,其中n
是字符串数,c
是每个字符串中的字符数。但是,下面的算法会多次访问多个数组。如果我在其中的每一个上增加一个计数器,我最终会得到nc
的重要因子。
那么在算法的上下文中究竟什么构成了“数组访问”?是否只有一个数组访问实例被认为更重要,我应该在这里计算,或者这个示例实际上是一个低效的实现,它使用了比必要更多的数组访问?
public int lsdSort(String[] array, int W)
int access = 0;
// Sort a[] on leading W characters.
int N = array.length;
String[] aux = new String[N];
for (int d = W-1; d >= 0; d--)
// Sort by key-indexed counting on dth char.
int[] count = new int[R+1]; // Compute frequency counts.
for (int i = 0; i < N; i++)
count[array[i].charAt(d) + 1]++;
for (int r = 0; r < R; r++)
// Transform counts to indices.
count[r+1] += count[r];
for (int i = 0; i < N; i++)
// Distribute.
aux[count[array[i].charAt(d)]++] = array[i];
for (int i = 0; i < N; i++) // Copy back.
array[i] = aux[i];
return access;
【问题讨论】:
感谢 Yuval 为提高可读性所做的小修改! 【参考方案1】:我读过 LSD 排序需要 n 次 c 数组访问,其中 n 是字符串的数量,c 是每个字符串中的字符数。
你确定你没有读到它是O(nc)
吗?那根本不是一回事。这是big-O notation。关键不是要确定数组访问的确切数量——而是要讨论它如何增长(或者更确切地说,它如何增长的限制)随着n
或c
的增加。在这种情况下,它会线性增加 - 如果您将 n
增加 1000 倍,您只会期望总成本也会增加 1000 倍......而如果它是 O(n2c) 算法,它可能会增长 1,000,000 倍。 (严格来说,任何 O(nc) 算法也是 O(n2c),因为它只是一个限制,但我们不要讨论这个。)
【讨论】:
感谢您的回答。这似乎是有道理的。我最近才开始研究算法,并且熟悉大 O 符号。然而,我的教科书看起来好像基数排序只需要对每个字符串n
进行精确的 c
数组访问,尽管提供了这个实现示例,这让我感到困惑。
虽然我真的看不出nc
不可行的任何理由。您只需要 c
循环遍历数组并将值放入辅助数组。好吧,你可以说那是2nc
,因为我们必须写回字符,但是不同的数组;-)
@Parusa:这可能就是它的真正含义,而且确实有可能。不过,它似乎没有大 O 位那么 有趣。
@JonSkeet 我猜这本书只是想告诉我(尽管以一种令人困惑的方式)基数排序可以在线性时间排序,而且它实际上可以做到这一点在严格的 c.n 中,但实际效率取决于实施。感谢您的宝贵时间!【参考方案2】:
第一个 for 循环内的所有数组访问基本上都计为数组访问的总和,所以这就是你的 c. n 是您执行这些组合数组访问的次数。这可以让您大致了解函数的增长,而不是实际的访问次数。
【讨论】:
【参考方案3】:public int lsdSort(String[] array, int W)
int access = 0;
// Sort a[] on leading W characters.
int N = array.length;
String[] aux = new String[N];
for (int d = W-1; d >= 0; d--)
// Sort by key-indexed counting on dth char.
int[] count = new int[R+1]; // Compute frequency counts.
for (int i = 0; i < N; i++)
count[array[i].charAt(d) + 1]++;
access++;
access++;
for (int r = 0; r < R; r++)
// Transform counts to indices.
count[r+1] += count[r];
access++;
for (int i = 0; i < N; i++)
// Distribute.
aux[count[array[i].charAt(d)]++] = array[i];
access++;
access++;
access++;
for (int i = 0; i < N; i++) // Copy back.
array[i] = aux[i];
access++;
access++;
return access;
数组“访问”是读取或写入...
【讨论】:
+1 为例。我没有考虑在同一个循环中增加几次。谢谢你的回答!【参考方案4】:在Big-O 渐近符号中,访问次数与一个常数成正比。当你分析代码时,所有的常量都被丢弃了。
在基数排序的情况下,大 O 是O(cn)
。但是,如果您想实际计算访问数组的次数,则需要将该数字乘以某个常量 k
,其中 k
特定于具体的编码实现。
例如,这个函数是O(n)
,但数组被访问的次数是2n
:一次用于读取值,一次用于更新值。号码2
被丢弃。
for (i=0; i<N; i++)
A[i] = A[i] + 1;
【讨论】:
以上是关于在算法的上下文中啥构成“数组访问”?的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 协程协程上下文 ( 协程上下文构成要素 | 指定协程上下文元素组合 | 协程上下文元素的继承关系 | 协程上下文元素的几种指定形式 | 默认 | 继承 | 自定义指定 )