Creating a sequence of unique random digits

1.3k Views Asked by At

I have the following code

use strict;
use warnings;
use 5.22.0;

# Generating random seed using 
# Programming Perl p. 955
srand( time() ^ ($$ + ($$ << 15 ) ) );

# Generating code that could have duplicates
my @code = (
    (int(rand(9)) + 1),
    (int(rand(9)) + 1),
    (int(rand(9)) + 1),
    (int(rand(9)) + 1)
);

# Trying to remove duplicates and choosing the unique code
my %seen = ();
my @unique = grep { ! $seen{ $_ }++ } @code;
say @unique;

I am generating a list of four random numbers, and I need to make sure that all four numbers are unique. I am able to scrape out the unique numbers but it is not always maintaining a scalar length of 4.

My initial thoughts were to do a foreach loop checking to see if each element is the same, but there has to be a quicker way to do this.

This is my initial thought (not using the unique set)

my $index = 0
foreach my $element (@code) {
    if ($element == $code[index]) {  
        # repopulate @code at said element
        $code[$index] = (int(rand(9)) + 1);
    }
    $index++;
 }

However, I believe that this might give me the same issue in that there might be duplicates.

Is there a quicker way to do this while maintaining four numbers in my array that are all unique?

2

There are 2 best solutions below

1
On BEST ANSWER

To generate a list of four unique non-zero decimal digits, use shuffle from List::Util and pick the first four

Like this

use strict;
use warnings;
use 5.010;

use List::Util 'shuffle';

my @unique = (shuffle 1 .. 9)[0..3];

say "@unique";

output

8 5 1 4

There's no need to seed the random number generator as Perl does it for you. Only use srand if you need a repeatable random sequence


Update

Here's another way that's similar to what you already had. Essentially it just keeps making random numbers until it has four different ones

use strict;
use warnings;
use 5.010;

my %unique;
++$unique{int(rand 9) + 1} while keys %unique < 4;

say join ' ', keys %unique;
0
On

@Boromir gave you what is probably the cleanest solution. If you do need to use rand(), just make sue no duplicates are saved in the first place instead of removing them later:

#!/usr/bin/perl
use strict;
use warnings;
use 5.22.0;
# Generating random seed using 
# Programming Perl p. 955
srand(time()^($$+($$<<15)));

#Generating code that can't have duplicates
my %seen = ();
while (scalar(keys(%seen)!=4)) {
    $seen{int(rand(9)) + 1}++
}
say keys %seen;