countingcrows has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

I need to have a bunch of IPs from a text file randomized in Perl.

I have the following bogus IPs in the input file:

#Group A 184.75.65.68 #Group B 184.75.122.146 184.75.122.147 184.75.122.148 #Group C 64.3.71.98 64.3.71.99 64.3.71.100 64.3.71.106 #Group D 64.3.73.17 64.3.73.18 64.3.73.19 64.3.73.20 #Group E 66.1.73.21 66.1.73.22 66.1.73.23

I want these IPs randomized by first Class A, then Class-B, then Class-C.

So the output can be something like:

(a random IP from each group, groups should be random as well, but they can repeat until their contents are all exchausted)

184.75.65.68 random from Group A 64.3.71.106 random from Group C 184.75.122.147 random from Group B 64.3.73.17 random from Group D 66.1.73.21 random from Group E random from Group A (obviously nothing since only 1 IP was there) random from Group C random from Group B random from Group D random from Group E and so on

The number of IPs and the number of subnets are not known in the file. They might be in different numbers. Also the IPs in each subnet are not be in equal numbers.

Can some of you experts think of a way? All my attempts have been futile.

I am willing to pay for a solution.

Thanks for your time.

Jenny

Replies are listed 'Best First'.
Re: Help with sorting/randomizing?
by GrandFather (Saint) on Mar 25, 2012 at 19:53 UTC

    Use a nested hash with the top level key the class A addresses, class B as the second tier, class C as the third tier and an array of class D addresses as the ultimate value.

    If that doesn't make sense write a little code, show us where you are having trouble, and we'll help further. PerlMonks is not a code writing service, but we will write almost any amount of code if we think you are making an effort and learning from it.

    You may find perllol helpful if you haven't used nested data structures in Perl before. To a reasonable extent you can replace [...] with {...} when using nested hashes instead of nested arrays. Note too the "See Also" section at the end.

    True laziness is hard work
      Thank you Grandfather. I don't really know where to start or how to start really. This is beyond me. If someone could show me a bit of code, I think I can pick up from there. I know what perlmonks is, I have often turned here for help in the past. But this issue I am not able to solve, and I thought someone experienced here could give me a hand (for a donation, or whatever). Thanks. J

        In your OP you say "All my attempts have been futile." which implies you have attempted a solution. Show us your last attempt and describe the problem you had with it.

        Did I say PerlMonks isn't a code writing service?

        Welcome along to PerlMonks by the. Were you here before as an Anonymous Monk, or have you gotten yourself a new name?

        True laziness is hard work
Re: Help with sorting/randomizing?
by druthb (Beadle) on Mar 25, 2012 at 19:42 UTC

    Off the top of my head, I'd say put the addresses in a hash of arrays, with the hash keys being the classes, and the array size therefore being the number of addresses in that class. Then get a random member of the array for a random hash key, and delete that array member. If the array becomes empty, delete the hash key so you can't pick that one any more. Repeat until there are no hash keys left in the hash.

    There might be more-efficient ways to do that, but that's where I'd start.

    UPDATE: I've got an idea on this; should have a code snippet for you later this evening!

    D Ruth Bavousett
Re: Help with sorting/randomizing?
by JavaFan (Canon) on Mar 25, 2012 at 20:49 UTC
    my @groups; while (<DATA>) { chomp; next unless /\S/; if (/^#/) { push @groups, []; next; } push @{$groups[-1]}, $_; } while (@groups) { my $group = shift @groups; my $ip = splice @$group, rand @$group, 1; say $ip; push @groups, $group if @$group; } __DATA__ ... your list goes here ...
    Now, if you have a huge number of ip addresses in a single group, this is inefficient (it will be quadratic in the size of the largest group), but in most cases, this will do.

      JavaFan thank you. This is working well. However, Class-A is not being randomized.

      184... 64.3.71... 64.3.73... 66...
      What if the file has no group boundaries with hash sign?
        Class-A is not being randomized.
        How can you tell? There's only one element in this group.
        What if the file has no group boundaries with hash sign?
        Then you adapt the program to use whatever your file is using distinguish between groups. Djees man, do you expect me to infer from you single example how the rest of your data may look like?
Re: Help with sorting/randomizing?
by countingcrows (Initiate) on Mar 26, 2012 at 03:33 UTC

    Grandfather, is this the right direction?

    while (<IN>) { if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)/) { my $a = $1; my $b = $2; my $c = $3; my $d = $4; my $ip = "$a.$b.$c.$d"; my $classc = "$a.$b.$c"; my $classb = "$a.$b"; my $classa = "$a"; push(@{$hash{$classa}{$classb}{$classc}}, $ip); } } for my $a ( keys %hash ) { for my $b ( keys %{$hash{$a}} ) { for my $c ( keys %{$hash{$a}{$b}} ) { for my $ip ( @{$hash{$a}{$b}{$c}} ) { print "$ip\n"; } } } } use Data::Dumper; print Dumper \%hash;

      Yep, that's heading in the direction I had been thinking of. However that doesn't really generate the random picking that I think you want. Consider the following:

      use strict; use warnings; my %ips; while (<DATA>) { next if ! /^(\d+)\.(\d+)\.(\d+)\.(\d+)/; push @{$ips{$1}{$2}{$3}}, $4; } my @selected; while (my @picks = pickEm(\%ips)) { push @selected, @picks; } print join "\n", @selected; sub pickEm { my ($root) = @_; return splice @$root, rand(@$root), 1 if 'ARRAY' eq ref $root; my @keys = keys %$root; while (@keys) { my $key = splice @keys, rand(@keys), 1; my $pick = pickEm($root->{$key}); return "$key.$pick" if defined $pick; delete $root->{$key}; } return; } __DATA__ #Group A 184.75.65.68 #Group B 184.75.122.146 184.75.122.147 184.75.122.148 #Group C 64.3.71.98 64.3.71.99 64.3.71.100 64.3.71.106 #Group D 64.3.73.17 64.3.73.18 64.3.73.19 64.3.73.20 #Group E 66.1.73.21 66.1.73.22 66.1.73.23

      Prints (for one random run):

      66.1.73.22 64.3.71.106 184.75.122.146 184.75.65.68 64.3.71.100 64.3.71.98 66.1.73.23 66.1.73.21 184.75.122.148 64.3.71.99 64.3.73.18 64.3.73.17 184.75.122.147 64.3.73.20 64.3.73.19
      True laziness is hard work

        Thanks Grandfather. Yes, I know it doesn't generate anything yet. I am trying to work on it to provide random ranges.

        This is all greek to me. I am thinking maybe counting %hash and iterating over the items.

        Your code provides the randomness. However, it still have sequential Class-C ranges that I am trying to avoid:

        64.3.71.100 64.3.71.98 66.1.73.23 66.1.73.21

        This script is for a firewall. It should not have consecutive Class-C's

        Best solution seems to be JavaFan's. If only I could shuffle Class-A's on that one

Re: Help with sorting/randomizing?
by Anonymous Monk on Mar 26, 2012 at 09:00 UTC

    I want these IPs randomized by first Class A, then Class-B, then Class-C.

    The other posters seem to have understood you, but I want to point out that you are using those terms wrong. You probably meant "randomised by the first octet, then the second, and then the third". Class A through E have to do with networks and subnetting. A class A network would be a network of 16 million IP addresses, and its address would be somewhere below 128.0.0.0. (Since the introduction of fine-grained subnetting, these classes have almost lost meaning.)

Re: Help with sorting/randomizing?
by ashish.kvarma (Monk) on Mar 26, 2012 at 04:39 UTC

    I probably should not give the complete code here as it goes against the the motto but couldn't resist the temptation in this case.

    I know there can be better solution to the problem in based on scenarios but here is my take on the issue.

    use strict; use warnings; use Data::Dumper; my %ips_hash; my $group = ''; # Create the data structure as # %h = ( # A => { # 184.75.65.68 => 1 # }, # B => { # 184.75.122.146 => 1, # 184.75.122.147 => 1, # }, # ); while (my $line = <DATA>) { if ($line =~ /#Group (\w+)/) { $group = $1; } elsif ($line =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) { $ips_hash{$group}{$1} = 1; } } # see if there is any group my $true = keys %ips_hash; while ($true) { # Get the random group my @groups = keys %ips_hash; $group = $groups[rand($#groups)]; # get the random ip my @ips = keys %{$ips_hash{$group}}; my $ip = $ips[rand $#ips]; print "$ip random from Group $group\n"; # delete the ip so that its not repeated delete $ips_hash{$group}{$ip}; # delete the group if it doesn't have any ips if ($#ips < 1) { delete $ips_hash{$group}; } $true = keys %ips_hash; } __DATA__ #Group A 184.75.65.68 #Group B 184.75.122.146 184.75.122.147 184.75.122.148 #Group C 64.3.71.98 64.3.71.99 64.3.71.100 64.3.71.106 #Group D 64.3.73.17 64.3.73.18 64.3.73.19 64.3.73.20 #Group E 66.1.73.21 66.1.73.22 66.1.73.23
    Update:

    I want these IPs randomized by first Class A, then Class-B, then Class-C.

    Looking back at the problem statement,,, may not be exactly what was expected. Here is what it prints:

    184.75.65.68 random from Group A 64.3.71.99 random from Group C 64.3.71.100 random from Group C 66.1.73.21 random from Group E 64.3.73.20 random from Group D 66.1.73.23 random from Group E 64.3.73.19 random from Group D 64.3.71.106 random from Group C 64.3.71.98 random from Group C 64.3.73.17 random from Group D 66.1.73.22 random from Group E 64.3.73.18 random from Group D 184.75.122.148 random from Group B 184.75.122.146 random from Group B 184.75.122.147 random from Group B
    Regards,
    Ashish
      Ashish, thanks. But main thing I am trying to avoid is to have sequential class-c's which is abundant in your code. However, your code is giving me some clues to work on.