对于输入子序列S和数字k、2 <= k <= (length(S) + 1) / 2。我发现这个假设本身是必要的,因为它拒绝了子序列根本无法确定的情况,因此不可能是它们的最大值中的最小值。
我打算尝试消除第一个假设。但如果这会使算法更难证明其正确性或实施起来,我就不会这样做。
伪代码,版本 1
find_k_length_sequence_maxes_min (S, k)
if k < 2 or length(S) < 2 * k - 1
return NO_SUCH_MINIMUM
sorted = copy(S)
sort_ascending(sorted)
for t from k - 1 to length(S)
current_length = 0
index = find_index(S, sorted[t])
last_index = index
for u descending from index to 1
if u < last_index - 1 && S[u] <= sorted[t]
current_length += 1
last_index = u
if current_length >= k
return sorted[t]
last_index = index
for u ascending from index to length(S)
if u > last_index + 1 and S[u] <= sorted[t]
current_length += 1
last_index = u
if current_length >= k
return sorted[t]
伪代码,版本 2
(这与版本 1 中的算法相同,只是使用更自然的语言编写。)
(1) 令S 是一个整数序列,它的所有元素都是唯一的。
(2) 令“S 的非连续子序列”表示S 的任何两个元素在S 中不相邻的子序列。
(3) 令k 是满足2 <= k <= (length(S) + 1) / 2 的整数。
(4) 找出长度为k的S的所有非连续子序列的最大值中的最小值。
(4.1) 找到S 的最小元素,使其成为大小为k 的S 的非连续子序列的最大值。
(4.1.1) 设sorted 为S 的排列,其元素按升序排列。
(4.1.2) 对于sorted的每个元素e,检查它是否是长度为k的S的非连续子序列的最大值。如果是,请将其退回。
(4.1.2.1) 设x和y为整数,使得1 <= x <= index(minmax(k))和index(minmax(k)) <= y <= length(S)。
(4.1.2.2) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得e是它们中每一个的最大值。
(4.1.2.3)检查all(1, index(e))最长序列的长度是否大于等于k。如果是,返回e。
(4.1.2.4)检查all(1, index(e))的最长子序列长度和all(index(e), length(S))的最长子序列长度之和是否大于等于k。如果是,返回e。
正确性证明
我不记得我是否曾经为一个程序写过任何正确性证明,所以我很确定这个程序的质量是否可以改进。对它持保留态度。如果可以,我鼓励你自己检查算法是否正确。
(1) 词汇表:
-
“观察”是指不是从任何观察或结论中得出的陈述,不需要证明,
-
“结论”是指从至少一个观察或结论得出的陈述,不需要证明,
-
我所说的“定理”是指不是从任何观察或结论中得出的陈述,需要证明。
(2) 令S 是一个整数序列,使得它的所有元素都是唯一的。
(3) 令“S 的非连续子序列”表示S 的任何两个元素在S 中不相邻的子序列。
(4) 令k 是满足2 <= k <= (length(S) + 1) / 2 的整数。
(5) 令minmax(k) 是S 的一个元素,使得它是S 的所有长度为k 的非连续子序列的最大值中的最小值。
(6) (定理)minmax(k) 是S 的最小元素,使得它是长度为k 的S 的非连续子序列的最大值。
(7) 换句话说,S中没有元素小于minmax(k),这是S的长度为k的非连续子序列的最大值。
(8)((6)的证明)(观察)由于minmax(k)是S长度为k的所有非连续序列的最大值中的最小值,因此不存在S的非连续子序列长度为k,使其最大值大于minmax(k)。
(9)((6)的证明)(结论)如果(6),则S小于minmax(k)的任何元素不能是S长度为k的任何非连续子序列的最大值。
(10) ((6) 的证明) QED
(11) 设x和y是满足1 <= x <= index(minmax(k))和index(minmax(k)) <= y <= length(S)的整数。
(12) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得minmax(k)是它们中每一个的最大值。
(13)(观察)minmax(k)是all(1, length(S))的最长序列中的最大值。
(14) 这个观察结果可能看起来太微不足道了。但是,显然,考虑到最长的子序列而不是长度为 k 的子序列,我更容易编写和证明算法。因此,我认为这一观察值得注意。
(15) (定理)可以通过以下方式产生最长的all(1, index(minmax(k)))序列:
(16) ((15)的证明)让S 的“可能元素”表示小于或等于minmax(k) 并且与最后一个元素不相邻的元素。
(16a) ((15)的证明)设C为(15)中产生的子序列。
(17)(证明(15))(观察)
(18) ((15)的证明)令D 是all(1, index(minmax(k))) 的序列,使得length(D) > length(C)。
(19)((15)的证明)至少满足以下条件之一:
-
在第一个被取元素之前,D 中可能的元素少于 0 个,
-
在两个被取元素(不包括它们)之间使得它们之间在C中有 1 个可能的元素,在D 中有 0 个可能的元素,
-
在最后一个元素之后,D 中可能的元素少于 1 个。
(20)(证明(15))(观察)
-
在第一个被取元素之前不能有少于 0 个可能的元素,
-
如果D中的两个被取元素(不包括它们)之间可能的元素少于1,而C中有1,这意味着我们取了大于minmax(k)的元素,或者与minmax(k)相邻的元素最后采取的一个,与(12)相矛盾,
-
如果D 中最后一个元素之间的可能元素少于 1 个,而在 C 中有 1 个元素,则意味着我们已经获取了大于 minmax(k) 的元素,或者与最后一个元素相邻的元素一,与(12)相矛盾。
(21) ((15) 的证明) QED
(22)(观察)(15) 也适用于all(index(minmax(k)), length(S))。
(23)(观察)length(all(1, length(S))) = length(all(1, index(minmax(k)))) + length(all(index(minmax(k)), length(S)))。
执行
如果任何 assert 调用都没有中止程序,则所有测试都会通过。
#include <limits.h> // For INT_MAX
#include <assert.h> // For assert
#include <string.h> // For memcpy
#include <stdlib.h> // For qsort
int compar (const void * first, const void * second) {
if (* (int *)first < * (int *)second) return -1;
else if (* (int *)first == * (int *)second) return 0;
else return 1;
}
void find_k_size_sequence_maxes_min (int array_length, int array[], int k, int * result_min) {
if (k < 2 || array_length < 2 * k - 1) return;
int sorted[array_length];
memcpy(sorted, array, sizeof (int) * array_length);
qsort(sorted, array_length, sizeof (int), compar);
for (int t = k - 1; t < array_length; ++t) {
int index = -1;
while (array[++index] != sorted[t]);
int size = 1;
int last_index = index;
for (int u = index; u >= 0; --u) {
if (u < last_index - 1 && array[u] <= sorted[t]) {
++size;
last_index = u;
}
if (size >= k) {
* result_min = sorted[t];
return;
}
}
last_index = index;
for (int u = index; u < array_length; ++u) {
if (u > last_index + 1 && array[u] <= sorted[t]) {
++size;
last_index = u;
}
if (size >= k) {
* result_min = sorted[t];
return;
}
}
}
}
int main (void) {
// Test case 1
int array1[] = { 6, 3, 5, 8, 1, 0, 9, 7, 4, 2, };
int array1_length = (int)((double)sizeof array1 / sizeof (int));
int k = 2;
int min = INT_MAX;
find_k_size_sequence_maxes_min(array1_length, array1, k, & min);
assert(min == 2);
// Test case 2
int array2[] = { 1, 7, 2, 3, 9, 11, 8, 14, };
int array2_length = (int)((double)sizeof array2 / sizeof (int));
k = 2;
min = INT_MAX;
find_k_size_sequence_maxes_min(array2_length, array2, k, & min);
assert(min == 2);
// Test case 3
k = 3;
min = INT_MAX;
find_k_size_sequence_maxes_min(array2_length, array2, k, & min);
assert(min == 8);
// Test case 4
k = 4;
min = INT_MAX;
find_k_size_sequence_maxes_min(array2_length, array2, k, & min);
assert(min == 9);
// Test case 5
int array3[] = { 3, 5, 4, 0, 8, 2, };
int array3_length = (int)((double)sizeof array3 / sizeof (int));
k = 3;
min = INT_MAX;
find_k_size_sequence_maxes_min(array3_length, array3, k, & min);
assert(min == 3);
// Test case 6
int array4[] = { 18, 21, 20, 6 };
int array4_length = (int)((double)sizeof array4 / sizeof (int));
k = 2;
min = INT_MAX;
find_k_size_sequence_maxes_min(array4_length, array4, k, & min);
assert(min == 18);
// Test case 7
int array5_length = 1000000;
int array5[array5_length];
for (int m = array5_length - 1; m >= 0; --m) array5[m] = m;
k = 100;
min = INT_MAX;
find_k_size_sequence_maxes_min(array5_length, array5, k, & min);
assert(min == 198);
}
如果您有任何问题或提示,或者看到任何错误或错误,请发表评论。
编辑:正如我所写的,我试图消除第一个假设。我觉得我成功了,就是这个假设可以去掉。
只需要很少的改动。值得注意的是,现在我使用不定冠词“a”出现的所有术语“最小”和“最大”。我想表达的是,S 中可能有不止一个元素具有最小值,S 中可能有不止一个元素具有最大值。
伪代码,没有元素唯一性的版本 1
线
index = find_index(S, sorted[t])
应替换为该行
index = find_first_index(S, sorted[t])
伪代码,没有元素唯一性的版本 2
(这与版本 1 中的算法相同,只是使用更自然的语言编写。)
(1) 设S为整数序列。
(2) 令“S 的非连续子序列”表示S 的任何两个元素在S 中不相邻的子序列。
(3) 令k 是满足2 <= k <= (length(S) + 1) / 2 的整数。
(4) 找出长度为k的S的所有非连续子序列的最大值中的最小值。
(4.1) 找到S 的最小元素,使得它是大小为k 的S 的非连续子序列的最大值。
(4.1.1) 设sorted 为S 的排列,其元素按升序排列。
(4.1.2) 对于sorted的每个元素e,检查它是否是长度为k的S的非连续子序列的最大值。如果是,请将其退回。
(4.1.2.1) 设x和y为整数,使得1 <= x <= index(minmax(k))和index(minmax(k)) <= y <= length(S)。
(4.1.2.2) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得e是其中每一个的最大值。
(4.1.2.3)检查all(1, index(e))最长序列的长度是否大于等于k。如果是,返回e。
(4.1.2.4)检查all(1, index(e))的最长子序列长度和all(index(e), length(S))的最长子序列长度之和是否大于等于k。如果是,返回e。
没有元素唯一性的证明
第 (2) 点现在应该是:
(2) 设S为整数序列。
第 (5) 点现在应该是:
(5) 令minmax(k) 是S 的一个元素,使得它是S 的所有非连续子序列中长度为k 的最大值中的最小值。
第 (8) 点现在应该是:
(8)((6)的证明)(观察)由于minmax(k)是长度为k的S的所有非连续序列的最大值中的最小值,因此不存在S的非连续子序列长度为k,使其最大值大于minmax(k)。
第 (12) 点现在应该是:
(12) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得minmax(k)是其中每一个的最大值。
没有元素唯一性的实现
应该添加以下测试用例:
// Test case 8 (no uniqueness)
int array6[] = { 18, 21, 21, 6 };
int array6_length = (int)((double)sizeof array6 / sizeof (int));
k = 2;
min = INT_MAX;
find_k_size_sequence_maxes_min(array6_length, array6, k, & min);
assert(min == 18);
// Test case 9 (no uniqueness)
int array7[] = { 18, 21, 18, 6 };
int array7_length = (int)((double)sizeof array7 / sizeof (int));
k = 2;
min = INT_MAX;
find_k_size_sequence_maxes_min(array7_length, array7, k, & min);
assert(min == 18);
// Test case 9 (no uniqueness)
int array8[] = { 18, 18, 20, 6 };
int array8_length = (int)((double)sizeof array8 / sizeof (int));
k = 2;
min = INT_MAX;
find_k_size_sequence_maxes_min(array8_length, array8, k, & min);
assert(min == 18);
// Test case 10 (no uniqueness)
int array9[] = { 18, 18, 21, 6 };
int array9_length = (int)((double)sizeof array9 / sizeof (int));
k = 2;
min = INT_MAX;
find_k_size_sequence_maxes_min(array9_length, array9, k, & min);
assert(min == 18);