我试图在大约1.5n次操作中确定Rust中整数向量中的最大和第二大元素。(这是我正在读的一本书的练习)。
我的方法是迭代数组直到n/2以选择一对整数,然后比较整数以确定哪个更大。较大的数字被放入一个新的数组L,而较小的数字被添加到另一个数组S。
逻辑是最大的数字应该在数组L中,而第二大的数字应该在数组L或数组S中。对此将使用不同的比较。
第一步应该进行n/2次操作,而其他步骤理想地应该进行少于n次操作,这将是大约1.5n次操作。
我用Rust写了一个程序,这个程序有时会输出正确的答案,但大多数时候,它会输出错误的答案。
我试过调试它,发现了一些问题:
- 当迭代到***n/2***时,当向量的大小为奇数时,程序无法访问向量中的最后一个元素。如果它迭代到***(n +1)/2***,我会得到一个越界错误。
- 代码不能确定最大和第二大数字是否具有相同的索引。(它们不应该是相同的索引,这更多的是我缺乏如何实现这一点的想法)。
以下是我开发的程序:
pub fn max_pairwise_product_faster(a: &Vec<i64>) -> i64 {
let n = (a.len()) / 2;
let mut larger_numbers: Vec<i64> = Vec::new();
let mut smaller_numbers: Vec<i64> = Vec::new();
println!("n: {}", n);
for i in 0..n {
if a[2 * i] > a[2 * i + 1] {
larger_numbers.push(a[2 * i]);
smaller_numbers.push(a[2 * i + 1]);
} else {
larger_numbers.push(a[2 * i * 1]);
smaller_numbers.push(a[2 * i]);
}
}
println!(
"larger_numbers: {:?}, smaller_numbers: {:?}",
larger_numbers, smaller_numbers
);
let largest = larger_numbers.iter().max().unwrap();
// Determine the second-largest number
let mut second_largest = smaller_numbers.iter().max().unwrap();
//Check if there is any number higher than this in the larger_number vector
for number in &larger_numbers {
if number != largest && number > second_largest {
second_largest = number;
}
}
println!(
"largest_number: {}, second_largest_number: {}",
largest, second_largest
);
largest * second_largest
}
下面是当前实现的示例输入和输出:
input array: [3, 5, 8, 20, 8, 7, 11, 20, 5, 16]
n: 5
larger_numbers: [3, 8, 8, 11, 5], smaller_numbers: [3, 8, 7, 11, 5]
largest_number: 11, second_largest_number: 11
Wrong answer -> naive: 400, fast: 121
在这个例子中,正确答案应该是400,但是代码甚至没有正确地得到最大的数字。
我不想为这一点,但我如何实现这一步通过确切的代码。
5条答案
按热度按时间svmlkihl1#
算法
我希望@greybeard做出了关键的观察in their answer:
无论如何,如果您将每个元素与当前第二大值进行比较,我预计与最大值进行比较的可能性会越来越小。
原因是如果输入是随机的,则:
在极端情况下,假设最大的两个值是您开始的前两个值:
当然,你不会立即知道最大的或第二大的。平均而言,在查看了50%的元素之后,您就会知道它们,因此,您将在前半部分对每个元素进行2次比较,在后半部分对每个元素进行1次比较,总共进行1.5N次比较。
生 rust
我们应该有一个签名的形式:
首先,如果
values
为空,则不会有任何类型的乘积。其次,注意使用
&[i64]
作为参数,而不是&Vec<i64>
:它更通用。你可以从一个Vec<T>
,一个VecDeque<T>
,一个[T; N]
得到一个&[T]
,...命令
让我们先写命令式代码(Playground link):
功能
不过,Rust确实有更多的技巧,特别是模式匹配(更多)和用
Iterator
方法替换循环可以产生非常好的代码。同样的算法实际上可以表示为(Playground link):
这是在切片上使用模式匹配来以可视化的方式提取元素(尽管
tail @ ..
部分目前只在夜间使用),以及强大的fold
算法。kknvjkwl2#
我不知道你的算法有什么用。只使用两个变量并且只迭代一次似乎更有效率。
jm81lzqq3#
我不知道如何在 * 预期 * 比较次数为1.5n 的情况下 * 获得第二大 *(或 * 两个最大 ),或优于2n最坏情况 *。
在
0, 1, 2, 2
中,* 最大的两个 * 是2, 2
,但我在这里称1
* 第二大 *。无论如何,如果你将每个元素与当前的 * 第二大 * 进行比较,我预计与最大值的比较将变得越来越不可能。
对于“均匀随机输入”,我期望n+n比较,k甚至不是1/2。
关于找到最大的产品有很多问题,很少有比较和答案。
nc1teljy4#
你几乎有了,你的担心是正确的。它们可以在逻辑上消除。
首先,您可以简单地检查最后一个元素的长度是否为奇数。这是最坏情况下的两个比较。
第二,可以跟踪max元素的索引。
我把你的算法换了一下,让它更简单。我使用原始切片进行存储,使前半部分为较大的数字,后半部分为较小的数字。然后,我在前半部分运行朴素算法。这给了我两个数字和指数。然后,我检查与max的索引对应的索引处的较小的一半,因为这是较小的一半中唯一可能大于第二个max的数字。
最后,检查最后一个元素的长度是否为奇数。
这个算法在第一阶段进行N/2次比较,就像你的算法一样。第二阶段在列表的一半上使用2N算法。然后有三个额外的比较。总的来说,这是N/2 + 2N/2 + 3或0(1.5N)。
dwthyt8l5#
你可以做比1.5n更好的比较。
要理解的关键点是这样的:为了确定最大的元素,您必须在某个时候将其与第二大元素进行比较。
您可以使用此方法仅在n + ceil(log 2 n)- 2次比较中获得最大值和第二大值。
首先,为了找到最大的元素,将元素分成对,比较每对中的元素,并选择所有ceil(n/2)个获胜者。对获胜者重复此过程,直到只剩下最大的元素。这就像是淘汰赛
这个寻找最大元素的过程需要ceil(log 2n)轮,并且一起进行n-1次比较。在每一轮中,最大的元素与它配对的元素进行最多一次比较。收集这些配对并使用ceil(log 2n)-1比较来找到其中最大的。这将是第二大因素。