问题要求在一个排他范围内的随机字符串。下面的解决方案给出了一个包含范围内的随机字符串。结果证明,求解一个包含范围要容易得多。如果我们想要一个独占范围,我们只需在生成的字符串落在范围的边界上时拒绝它,并生成另一个随机字符串,直到不在范围的边界上。我为此添加了函数randstrx()。
为了说明我的解决方案,我们考虑十进制数字字符串,即其字符在C = [0 9] 范围内。例如任意长度的00127、66501、23、1等。考虑给定长度的所有字符串,例如4。显然,这些字符串上存在顺序。例如,对于我们的十进制数字字符串,长度为4 的字符串按如下顺序排列:0000、0001、0002、...、9999。
给定任何给定长度的字符串,很容易计算在字符串顺序中紧随其后或紧随其前的相同长度的字符串(我们忽略没有紧随其后或紧随其后的两种边界情况,因为我们将不需要它们)。对于我们的十进制数字字符串,我们只需添加或减去1。加法和减法在任何基数中都有很好的定义,数字可以由任何符号组成,它们不必是十进制数字。因此,这定义了如何将1(或一个单位)添加或减去给定长度的任何字符串。我们将在下面使用它。
让S1 和S2 分别是字符串范围的下限和上限。我们有S1 <= S2;订单由strcmp() 定义。 L1 是S1 的长度,L2 是S2 的长度。我们想在[S1 S2] 范围内生成一个随机字符串S。 S 可以是任意长度L <= M,其中M 是给定的常数。所有字符串都包含C = [C1 C2]范围内的字符。
[步骤 1] 第一步包括为S 生成随机长度L。根据S1 和S2 的值,并非所有长度都是可能的。事实上,S1 和S2 可以以相同的字符序列开头,例如[123555 1237]、S1 和S2 都以123 开头。假设长度为K >= 0 的Sk 是S1 和S2 开头的相等字符序列。设S1 = Sk + T1 和S2 = Sk + T2 其中T1 和T2 分别是S1 和S2 中Sk 之后的字符序列,+ 是字符串连接运算符。令N1 >= 0 和N2 >= 0 分别为T1 和T2 的长度。我们考虑 4 种情况:
-
N1 = 0 和 N2 = 0。 在这种情况下,S1 = S2 = Sk,因此范围 [S1 S2] 包含长度为 K 的单个字符串 Sk。因此,L = K 是 L 唯一可能的值。
-
N1 > 0 和 N2 = 0。 这种情况是不可能的,因为我们假设 S1 <= S2。
-
N1 = 0 和N2 > 0。 显然,L 必须至少是K 的公共字符序列的长度S1 和S2。要确定L 的上限,我们需要考虑T2。如果T2 可以递减,这意味着我们可以生成小于S2 的任意长度的字符串。例如,如果S1 = 123 和S2 = 123001,则12300099...9 小于S2(和> S1)。唯一不能减少T2 的情况是T2 = C1 + C1 + ... + C1。例如,如果S1 = 123 和S2 = 123000,则T2 不能递减。不可能生成小于S2 且长度大于L2 的字符串。所以在这种情况下,L 必须在[K L2] 范围内。
-
N1 > 0 和N2 > 0。 考虑L 的上限。我们知道T1 < T2 和T1 和T2 的第一个字符不相等,如果它们相等,那么共同的第一个字符将在共同的起始序列Sk 中。这意味着T1 可以递增或T2 可以递减。事实上,例如,在我们的十进制数字字符串中,唯一不能递增的T1 是99...9。如果T1 等于该值,则这也必须是T2 的值,但T1 将等于T2,并且T1 和T2 必须在@987654427 中@,矛盾。可以对T2 进行类似的推理。唯一不能递减的T2 是T2 = 00...0,因此T1 也必须是00...0,这会将T1 和T2 放入Sk,这是一个矛盾。因为T1 可以递增,T2 可以递减,所以我们可以生成大于T1 和小于T2 的随机字符串或任意长度。例如,如果S1 = 1231 和S2 = 1234,则字符串12311...1 和123399...9 落在[S1 S2] 范围内。考虑L 的下限。任何长度为K 的字符串都必须等于Sk,但由于N1 > 0,Sk 小于S1。所以随机字符串的长度不能是K。然而,很容易证明它的长度可以是K + 1。我们知道T1的第一个字符T1[0]小于T2的第一个字符,并且可以递增。因此,字符串Sk 附加字符T1[0] + 1 是长度为K + 1 的字符串,即大于S1。它也小于或等于S2。事实上,如果T2[0] = T1[0] + 1 和N2 = 1,那么随机字符串等于S2。向S2 添加更多字符只会使S2 大于随机字符串。因此,L 必须大于或等于 K + 1。
总而言之,我们有:
- 对于
N1 = 0 和N2 = 0,L = K。
- 对于
N1 > 0和N2 > 0,L > K。
- 对于
N1 = 0 和N2 > 0,如果T2 = 00...0,则L = [K L2],否则L >= K。
鉴于上述情况,我们为S 生成一个随机值L。
[步骤 2] 我们计算长度为L 的最小字符串S1L 大于或等于S1。如果L >= L1,则S1L 是S1 附加了足够的C1 字符以使其长度等于L。例如,如果S1 是23906 并且L 是8,S1L = 23906000。另一方面,如果L < L1,我们将S1截断为长度L并添加1,如上所述。例如,如果S1 是23906 而L 是3,S1L = 240。
[步骤 3] 我们计算长度为L 的最大字符串S2L,它小于或等于S2。如果L > L2,则S2L 是S2 减去1(如上所述)附加足够的C2 字符以使其长度等于L。例如,如果S2 是23906 并且L 是8,S2L = 23905999。另一方面,如果L < L2,我们将S2截断为长度L。例如,如果S2 是23906 而L 是3,S2L = 239。
[步骤 4] 现在我们已经有了长度为 L 的最小和最大字符串,它们都在 [S1 S2] 范围内,很容易生成长度为 @987654527 的随机字符串@在同一范围内。对于从0 到L – 1 的每个i,我们只需将S[i] 设置为[S1L[i] S2L[i]] 范围内的随机字符值。
这是实现该解决方案的 C 代码。
#include "stdlib.h"
#include "string.h"
#include "assert.h"
#include "time.h"
/* Generate a random string in inclusive range [S1 S2] of maximum length M. */
/* S1, S2, and the returned random string have their characters in the range [C1 C2]. */
/* Returns NULL if no string of maximum length M exists in [S1 S2]. */
char * randstr(char * S1, char * S2, int M, int C1, int C2)
{
/* Validate inputs. */
if (S1 == NULL || S2 == NULL || *S1 == 0 || *S2 == 0) return NULL;
if (strcmp(S1, S2) > 0) return NULL;
if (C1 > C2) return NULL;
/* Lengths of input strings. */
int L1 = strlen(S1);
int L2 = strlen(S2);
/* Length of longest common starting sequence of S1 and S2. */
int K = 0;
for (; K < L1 && K < L2 && S1[K] == S2[K]; K++);
/* Generate length of random string S. */
int L;
int N1 = L1 - K;
int N2 = L2 - K;
if (N1 == 0 && N2 == 0)
{
/* L = K */
if (M < K) return NULL;
L = K;
}
else if (N1 > 0 && N2 > 0)
{
/* L = [K + 1 M] */
if (M < K + 1) return NULL;
L = (rand() % (M - (K + 1) + 1)) + K + 1;
}
else if (N1 == 0 && N2 > 0)
{
char * T2 = S2 + K;
while (*T2 > 0 && *T2 == C1)
T2++;
if (*T2 == 0) /* T2 = C1 + C1 + ... + C1 */
{
/* L = [K L2] */
if (M < K) return NULL;
if (M < L2)
L = (rand() % (M - K + 1)) + K;
else
L = (rand() % (L2 - K + 1)) + K;
}
else
{
/* L >= K */
if (M < K) return NULL;
L = (rand() % (M - K + 1)) + K;
}
}
/* Compute smallest string S1L of length L that is greater than S1. */
char * S1L = (char*)malloc(sizeof(char) * (L + 1));
S1L[L] = 0;
if (L >= L1)
{
for (int i = 0; i < L1; S1L[i] = S1[i++]); /* Copy S1 into S1L. */
for (int i = L1; i < L; S1L[i++] = C1); /* Append C1 characters. */
}
else /* L < L1 */
{
for (int i = 0; i < L; S1L[i] = S1[i++]); /* Set S1L to S1 truncated to length L. */
for (int i = L - 1; i >= 0; i--) /* Increment S1L. */
{
if (S1L[i] < C2)
{
S1L[i]++;
break;
}
else
S1L[i] = C1;
}
}
/* Compute largest string S2L of length L that is less than S2. */
char * S2L = (char*)malloc(sizeof(char) * (L + 1));
S2L[L] = 0;
if (L > L2)
{
for (int i = 0; i < L2; S2L[i] = S2[i++]); /* Copy S2 into S2L. */
for (int i = L2; i < L; S2L[i++] = C2); /* Append C2 characters. */
for (int i = L2 - 1; i >= 0; i--) /* Decrement copy of S2. */
{
if (S2L[i] > C1)
{
S2L[i]--;
break;
}
else
S2L[i] = C2;
}
}
else /* L < L2 */
{
for (int i = 0; i < L; S2L[i] = S2[i++]); /* Set S2L to S2 truncated to length L. */
}
/* Generate random string S of length L in range [S1L S2L]. */
char * S = (char*)malloc(sizeof(char) * (L + 1));
S[L] = 0;
for(int i = 0; i < L; i++)
S[i] = (rand() % (S2L[i] - S1L[i] + 1)) + S1L[i];
free(S1L);
free(S2L);
return S;
}
/* Generate a random string in exclusive range (S1 S2) of maximum length M. */
/* S1, S2, and the returned random string have their characters in the range [C1 C2]. */
/* Returns NULL if no string of maximum length M exists in (S1 S2). */
char * randstrx(char * S1, char * S2, int M, int C1, int C2)
{
/* If S1 + 1 >= S2, then no random string exists in (S1 S2). */
int L1 = strlen(S1);
char * S = (char*)malloc(sizeof(char) * (L1 + 1));
strcpy(S, S1);
for (int i = L1 - 1; i >= 0; i--)
{
if (S[i] < C2)
{
S[i] += 1;
break;
}
}
if (strcmp(S, S2) >= 0)
{
free(S);
return NULL;
}
do
{
free(S);
S = randstr(S1, S2, M, C1, C2);
}
while (strcmp(S, S1) == 0 || strcmp(S, S2) == 0);
return S;
}
int main()
{
srand((unsigned int)time(NULL)); // initialisation de rand
char * s;
/* N1 == 0, N2 == 0, K == 1 */
s = randstr("0", "0", 10, '0', '9');
assert(strcmp(s, "0") == 0);
/* N1 == 0, N2 == 0, K == 4 */
s = randstr("1210", "1210", 10, '0', '9');
assert(strcmp(s, "1210") == 0);
/* N1 == 0, N2 == 1, K == 3, T2 == 0 */
s = randstr("121", "1210", 3, '0', '9');
assert(strcmp(s, "121") == 0);
/* N1 == 0, N2 == 1, K == 3, T2 == 0 */
s = randstr("121", "1210", 4, '0', '9');
assert(strcmp(s, "121") == 0 || strcmp(s, "1210") == 0);
/* N1 == 1, N2 == 1, K == 3, T2 != 0 */
s = randstr("1210", "1211", 4, '0', '9');
assert(strcmp(s, "1210") == 0 || strcmp(s, "1211") == 0);
/* N1 == 1, N2 == 1, K == 3, T2 != 0 */
s = randstr("1210", "1211", 5, '0', '9');
assert(strcmp(s, "1210") >= 0 || strcmp(s, "1211") <= 0);
/* N1 == 4, N2 == 1, K == 0, T2 != 0 */
s = randstr("0004", "1", 25, '0', '9');
assert(strcmp(s, "0004") >= 0 || strcmp(s, "1") <= 0);
return 0;
}