【问题标题】:How to generate non-repeating numbers in C?如何在C中生成不重复的数字?
【发布时间】:2021-02-22 12:26:18
【问题描述】:

我在这里的第一篇文章。

我正在尝试用 C 语言编写一个程序,它会生成一个由数字、字母和大写字母组成的随机密码。问题是密码中的字符不能重复。我尝试了几种方法来防止这种情况发生,但似乎没有任何效果。

void createPassword() {
char password[LENGTH];
char nums[] = "0123456789";
char letters[] = "abcdefghijklmnopqrstuvwxyz";
char caps[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int selector = rand() % 3; //random choice of character type
int i;

printf("Vytvorene heslo: ");
for(i = 0;i < LENGTH;i++) {
    if(selector == 1) { //if selector == 1, add number to password etc.
        password[i] = nums[rand() % 10];
        printf("%c", password[i]);
        selector = rand() % 3;
    }
    else if(selector == 2) {
        password[i] = letters[rand() % 26];
        printf("%c", password[i]);
        selector = rand() % 3;
    }
    else { 
        password[i] = caps[rand() % 26];
        printf("%c", password[i]);
        selector = rand() % 3;
    }
}}

如果有人能告诉我下一步该怎么做,我会很高兴。

【问题讨论】:

  • 请理解,通过人为地将随机抽取限制在字母表的任何特定子集,您实际上会使您的安全性更糟,而不是更好。
  • 注意:我首先将int selector = rand() % 3; 放入循环中(并且,在分配之前,您可以测试角色是否已经存在,然后再试一次)跨度>
  • 问题是密码中的字符不能重复这意味着它是NOT随机的。
  • "问题是密码中的字符不能重复。"作为观察,这意味着密码不是真正随机的,尽管从技术上讲 rand() 无论如何都不是真正随机的。但当然,这只是一种观察,而不是对您的密码方法的批评。
  • 致所有响应者:使用srand() 可能会导致非常不安全的密码。

标签: arrays c random passwords


【解决方案1】:

选择数组的随机索引与顺序选择随机数组的值相同。我使用 Fisher–Yates shuffle Algorithm 对数组进行洗牌。生成混洗数组后,只需从混洗数组中选择下一个字符的索引,并使用symbols[] 从中访问相应的字符。还。我使用srand(time(0)) 为随机数生成器提供了一个随机种子。包括 time.h 以使用 time(0)

void createPassword() {
    char password[LENGTH];
    int total = 10+26+26;
    char symbols[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int i;
    int ar[total];
    for(i = 0; i < total; i++){
        ar[i] = i;
    }
    srand(time(0));
    for (i = total-1; i >= 1; i--){

        // get random 0 <= temp <= i
        int temp = rand() % (i+1);
        
        // Swap ar[temp] and ar[i]
        int temp2 = ar[i];
        ar[i] = ar[temp];
        ar[temp] = temp2;
    }
    printf("Vytvorene heslo: ");
    for(i = 0;i < LENGTH;i++) {
        password[i] = symbols[ar[i]];
        printf("%c", password[i]);
    }
}

【讨论】:

  • 考虑total -> sizeof(symbols) - 1
【解决方案2】:

这没什么大不了的,随机种子在 C 中多次运行是相似的。

您可能需要将随机种子设置为time(0),方法是首先添加如下代码:

srand(time(0));

你也应该导入time.h

#include <time.h>

此外,如果您想同时创建一个由数字和字母组成的密码,您可能需要将selector 赋值移动到循环中。

【讨论】:

  • @vacexpfleger 正确;那是它所属的地方;正如公认的答案所暗示的那样,不在生成器函数中。 srand 绝不应该在潜在可重复的代码路径中,除非重复伪随机序列的具体原因是目标(即有意图的硬播种)。简而言之,您(已经)做对了,接受的答案不应该有,但我假设您已经知道了。
【解决方案3】:

一种简单的强制随机数不重复字母的方法可以通过使用元素数组来实现,这将起到卡片组的作用。

每次你得到一张卡片,你都会从牌组的其余部分中得到它(有一点可以区分已经提取的卡片和仍然要输出的卡片)你选择一个介于 0 到 n- 之间的随机数1 其中 n-1 是牌组中剩余的牌张数。提取后,将提取的卡片与仍要提取的组中的第一个切换,并将点向前移动一个位置,因此它已被提取并且不再被选中。

示例实现如下所示,在函数extract 中,它使用cells 的数组来存储要打印的可用对象(它们可以是任何东西,在给定的实现中它们是char能够产生你想要的——不重复字符的随机字符串):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

typedef char cell;

/* cell is a type (the above definition allows to be generic)
 * array is the array of cards still to be output, n is its size.
 * which is the card we select from the array, so it must be in
 * range 0..n-1. */
cell extract(cell *array, size_t n, int which)
{
    if (which) {
        /* exchange position n with position 0 */
        cell temp = array[0];
        array[0] = array[which];
        array[which] = temp;
    }
    return array[0];
}

/* this is a simple main program to illustrate how to use the
 * function above. */
int main(int argc, char **argv)
{
    unsigned N = 10;
    unsigned seed = 0;
    /* we use an array, because we need to modify it. */
    char alpha[1024] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int opt;
    int show_seed = 0;

    while ((opt = getopt(argc, argv, "n:s:a:S")) != EOF) {
        switch (opt) {
        case 'n': N = atoi(optarg); break;
        case 's': seed = atoi(optarg); break;
        case 'S': show_seed = 1; break;
        case 'a': strncpy(alpha, optarg, sizeof alpha); break;
        }
    }

    if (seed) srand(seed);
    else {
        sranddev();
        srand(seed = (unsigned)rand());
        if (show_seed) {
            fprintf(stderr,
                    "seed = %u\n",
                    seed);
        }
    }

    argc -= optind;
    argv += optind;

    int len;
    cell *pos;
    for (pos = alpha, len = strlen(alpha);
        *pos && len && N--;
        pos++, len--)
    {
        putchar(extract(pos, len, rand() % len));
    }
    puts(""); /* final \n */
}

程序的用法是:

deck [ -a string  ][ -S ][ -s seed ][ -n N ]
where:
  -a allows you to specify the string where characters will be taken from.  Defaults to ABCDEF...XYZ
  -S prints the seed used in the run, so you can specify it to initialize the random number generator.
  -s specifies a seed from a previous run to generate the same sequence.  Defaults to a random initialization based on sranddev() (FreeBSD)
  -n specifies the number of characters to select from the string.  Defaults to 10.

一个示例运行是:

$ deck -s 123456
UKWOACYZLI
$ deck -s 123456 -n 26
UKWOACYZLITPJHQESVGMRBXFDN
$ _

如您所见,没有重复的字符。

【讨论】:

    猜你喜欢
    • 2011-01-02
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    • 2017-05-23
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    相关资源
    最近更新 更多