Entrelacement tableaux triés rares

voix
7

J'ai un ensemble de listes d'événements. Les événements se produisent toujours dans un ordre donné, mais pas chaque événement arrive toujours. Voici un exemple d'entrée:

[[ do, re, fa, ti ],
 [ do, re, mi ],
 [ do, la, ti, za ],
 [ mi, fa ],
 [ re, so, za ]]

Les valeurs d'entrée ne sont pas d'ordre inhérent. Ils sont en fait des messages tels que « créer des liens symboliques » et « réindexation recherche ». Ils sont classés dans la liste individuelle, mais il n'y a aucun moyen de regarder seulement « fa » dans la première liste et « mi » dans la seconde et de déterminer qui vient avant l'autre.

Je voudrais être en mesure de prendre cette entrée et générer une liste triée de tous les événements:

[ do, re, mi, fa, so, la, ti, za ]

ou mieux encore, des informations sur chaque événement, comme un nombre:

[ [do, 3], [re, 3], [mi, 2],
  [fa, 2], [so, 1], [la, 1],
  [ti, 1], [za, 2] ]

Y at-il un nom pour ce que je fais? Y at-il des algorithmes acceptés? J'écris ceci en Perl, si cela importe, mais pseudocode ferai.

Je sais que , compte tenu de mon entrée exemple, je ne peut probablement pas être garantie de l'ordre « droit ». Mais mon entrée réelle a des tonnes plus datapoints, et je suis convaincu que certains l' intelligence , ce sera 95% à droite ( ce qui est vraiment tout ce que je dois). Je ne veux pas réinventer la roue si je n'ai pas.

Créé 09/07/2010 à 19:32
source utilisateur
Dans d'autres langues...                            


10 réponses

voix
0
perl -de 0
  DB<1> @a = ( ['a','b','c'], ['c','f'], ['h'] ) 
  DB<2> map { @m{@{$_}} = @$_ } @a
  DB<3> p keys %m
chabf

Quickiest raccourci je peux penser. De toute façon, vous devez itérer les choses au moins une fois ...

Créé 09/07/2010 à 19:42
source utilisateur

voix
0

C'est un candidat idéal pour un tri par fusion . Aller à la page de wikipedia ici pour une assez bonne représentation de l'algorithme http://en.wikipedia.org/wiki/Merge_sort

Ce que vous avez décrit est en fait un sous-ensemble / petit tweak du genre de fusion. Au lieu de commencer par un tableau non trié, vous disposez d'un ensemble de tableaux triés que vous souhaitez fusionner ensemble. Il suffit d'appeler la fonction « fusion » comme décrit dans la page wikipedia sur des paires de vos tableaux et les résultats de la fonction de fusion jusqu'à ce que vous avez un seul tableau (qui sera triée).

Pour modifier la sortie à la façon dont vous voulez, vous aurez besoin de définir une fonction de comparaison qui peut revenir si un événement est inférieur, égal ou supérieur à un événement différent. Puis, lorsque votre fonction de fusion trouve deux événements qui sont égaux, vous pouvez les réduire en un seul événement et tenir un compte pour cet événement.

Créé 09/07/2010 à 19:45
source utilisateur

voix
3

Théoriquement parlant, permettez-moi de suggérer l'algorithme suivant:

  1. Construire un graphe orienté.
  2. Pour chaque entrée [X, Y, Z], créez les bords X-> Y et Y-> Z si elles ne sont pas déjà là.
  3. Effectuer un tri topologique du graphe.
  4. Le tour est joué!

PS
Ceci est seulement en supposant que tous les événements se produisent dans un ordre spécifique (toujours!). Si ce n'est pas le cas, le problème devient NP-complet.

PPS
Et juste pour que vous avez quelque chose d' utile: Sort :: topologiques (ne sais pas si cela fonctionne réellement , mais il semble à droite)

Créé 09/07/2010 à 19:48
source utilisateur

voix
0

En gros, le nom que je lui donnerais est « hachage ». Vous mettez les choses en paires de valeurs de nom. Si vous voulez garder un semblant d'ordre, vous devez compléter le hachage avec un tableau qui maintient l'ordre. Cet ordre est « ordre de rencontre » pour moi.

use strict;
use warnings;

my $all 
    = [[ 'do', 're', 'fa', 'ti' ],
       [ 'do', 're', 'mi' ],
       [ 'do', 'la', 'ti', 'za' ],
       [ 'mi', 'fa' ],
       [ 're', 'so', 'za' ]
     ];

my ( @order, %counts );

foreach my $list ( @$all ) { 
    foreach my $item ( @$list ) { 
        my $ref = \$counts{$item}; # autovivs to an *assignable* scalar.
        push @order, $item unless $$ref;
        $$ref++;
    }
}

foreach my $key ( @order ) { 
    print "$key: $counts{$key}\n";
}

# do: 3
# re: 3
# fa: 2
# ti: 2
# mi: 2
# la: 1
# za: 2
# so: 1

Il y a d'autres réponses comme celle-ci, mais le mien contient cette astuce autovivification propre.

Créé 09/07/2010 à 20:31
source utilisateur

voix
2

Si vous n'êtes pas en écrivant à beaucoup de code, vous pouvez utiliser l'utilitaire de ligne de commande unix tsort:

$ tsort -
do re
re fa
fa ti
do re
re mi
do la
la ti
ti za
mi fa
re so
so za

Ce qui est une liste de toutes les paires dans l'entrée de votre échantillon. Ce produit en sortie:

do
la
re
so
mi
fa
ti
za

qui est essentiellement ce que vous voulez.

Créé 09/07/2010 à 21:06
source utilisateur

voix
3

Vous pouvez utiliser tsortpour déduire un délai raisonnable, mais pas nécessairement l' ordre tri unique , (connu comme un ordre topologique ) de la commande que vous avez observé. Vous pouvez être intéressé par l' tsortutilisation originale de » , qui est une structure similaire à votre problème.

Notez que tsortnécessite un graphe acyclique. En termes de votre exemple, cela signifie que vous ne pouviez pas voir ne puis en re dans une séquence et re suivie do dans un autre.

#! /usr/bin/perl

use warnings;
use strict;

use IPC::Open2;

sub tsort {
  my($events) = @_;

  my $pid = open2 my $out, my $in, "tsort";

  foreach my $group (@$events) {
    foreach my $i (0 .. $#$group - 1) {
      print $in map "@$group[$i,$_]\n", $i+1 .. $#$group;
    }
  }

  close $in or warn "$0: close: $!";

  chomp(my @order = <$out>);
  my %order = map +(shift @order => $_), 0 .. $#order;
  wantarray ? %order : \%order;
}

Parce que vous avez décrit les données rares, le code ci - dessus fournit tsortle plus de renseignements possible sur les événements de la matrice de adjacence.

Ayant cette information, le calcul d'un histogramme et le tri de ses composants est simple:

my $events = [ ... ];

my %order = tsort $events;

my %seen;
do { ++$seen{$_} for @$_ } for @$events;

my @counts;
foreach my $event (sort { $order{$a} <=> $order{$b} } keys %seen) {
  push @counts => [ $event, $seen{$event} ];
  print "[ $counts[-1][0], $counts[-1][1] ]\n";
}

Pour l'entrée dans votre question que vous avez fourni, la sortie est

[Faire, 3]
[La, 1]
[Re, 3]
[ số 1 ]
[Mi, 2]
[Fa, 2]
[Ti, 2]
[Za, 2]

Cela semble drôle parce que nous savons l'ordre de solfège, mais re et la sont incomparables dans l' ordre partiel défini par $events: nous savons seulement qu'ils doivent tous deux venir après le faire.

Créé 09/07/2010 à 21:22
source utilisateur

voix
0

Je ne suis pas vraiment sûr de ce que ce serait appelé non plus, mais je me suis un moyen de trouver l'ordre donné le tableau de tableaux en entrée. Essentiellement, le pseudo-code est le suivant:

10 Trouver article premier dans tous les tableaux
20 Poussez sur une liste que
30 Retirer cet élément de tous les tableaux
40 Aller à 10 s'il y a des éléments de gauche

Voici un prototype:

#!/usr/bin/perl

use strict;

sub InList {
    my ($x, @list) = @_;
    for (@list) {
        return 1 if $x eq $_;
    }
    return 0;
}

sub Earliest {
    my @lists = @_;
    my $earliest;
    for (@lists) {
        if (@$_) {
            if (!$earliest
                || ($_->[0] ne $earliest && InList($earliest, @$_))) {

                $earliest = $_->[0];
            }
        }
    }
    return $earliest;
}

sub Remove {
    my ($x, @lists) = @_;

    for (@lists) {
        my $n = 0;
        while ($n < @$_) {
            if ($_->[$n] eq $x) {
                splice(@$_,$n,1);
            }
            else {
                $n++
            }
        }
    }
}

my $list = [
    [ 'do', 're', 'fa', 'ti' ],
    [ 'do', 're', 'mi' ],
    [ 'do', 'la', 'ti', 'za' ],
    [ 'mi', 'fa' ],
    [ 're', 'so', 'za' ]
];

my @items;

while (my $earliest = Earliest(@$list)) {
    push @items, $earliest;
    Remove($earliest, @$list);
}

print join(',', @items);

Sortie:

do, ré, mi, fa, la, ti, donc, za

Créé 09/07/2010 à 21:42
source utilisateur

voix
0

Solution:

Cela résout la question initiale avant qu'elle ne soit modifiée par le demandeur.


#!/usr/local/bin/perl -w
use strict; 

   main();

   sub main{
      # Changed your 3-dimensional array to a 2-dimensional array
      my @old = (
                   [ 'do', 're', 'fa', 'ti' ],
                   [ 'do', 're', 'mi' ],
                   [ 'do', 'la', 'ti', 'za' ],
                   [ 'mi', 'fa' ],
                   [ 're', 'so', 'za' ]
                );
      my %new;

      foreach my $row (0.. $#old ){                           # loop through each record (row)
         foreach my $col (0..$#{$old[$row]} ){                # loop through each element (col)                    
            $new{ ${$old[$row]}[$col] }{count}++;
            push @{ $new{${$old[$row]}[$col]}{position} } , [$row,$col];
         }
      }

      foreach my $key (sort keys %new){
         print "$key : $new{$key} " , "\n";                   # notice each value is a hash that we use for properties 
      }      
   } 

Comment faire pour récupérer Info:

   local $" = ', ';                       # pretty print ($") of array in quotes
   print $new{za}{count} , "\n";          # 2    - how many there were
   print "@{$new{za}{position}[1]} \n";   # 4,2  - position of the second occurrence
                                          #        remember it starts at 0   

Fondamentalement, nous créons une liste unique d'éléments dans le hachage. Pour chacun de ces éléments que nous avons une table de hachage « propriété », qui contient un scalaire countet un tableau pour la position. Le nombre d'éléments dans le tableau devrait varier, en fonction de combien d'occurrences de l'élément étaient dans l'original.

La propriété scalaire est pas vraiment nécessaire puisque vous pouvez toujours prendre le scalaire du positiontableau pour récupérer le même numéro. Remarque: si jamais vous ajouter / supprimer des éléments du tableau countet positionne seront pas en corrélation dans leur sens.

  • exemple: print scalar @{$new{za}{position}};vous donnera la même chose queprint $new{za}{count};
Créé 09/07/2010 à 22:20
source utilisateur

voix
0

Il suffit de réaliser votre question dit leur est pas d'ordre prédéterminé, donc cela ne peut pas être Relevent.

code Perl:

$list = [
    ['do', 're', 'fa', 'ti' ],
    ['do', 're', 'mi' ],
    ['do', 'la', 'ti', 'za' ],
    ['mi', 'fa' ],
    ['re', 'so', 'za' ]
];
%sid = map{($_,$n++)}qw/do re mi fa so la ti za/;

map{map{$k{$_}++}@$_}@$list;
push @$result,[$_,$k{$_}] for sort{$sid{$a}<=>$sid{$b}}keys%k;

print "[@$_]\n" for(@$result);

sortie:

[do 3]
[re 3]
[mi 2]
[fa 2]
[so 1]
[la 1]
[ti 2]
[za 2]
Créé 10/07/2010 à 16:32
source utilisateur

voix
1

Utilisez un hachage pour agréger.

my $notes= [[qw(do re fa ti)],
       [qw(do re mi)],
       [qw(do la ti za)],
       [qw(mi fa)],
       [qw(re so za)]];

my %out;
foreach my $list (@$notes)
{
  $out{$_}++ foreach @$list;
}

print "$_: $out{$_}\n" foreach sort keys %out;

Les rendements

do: 3
fa: 2
la: 1
mi: 2
re: 3
so: 1
ti: 2
za: 2

Le% sur hachage est facilement converti en une liste si c'est ce que vous voulez.

my @newout;
push @newout,[$_,$out{$_}] foreach sort keys %out;
Créé 21/04/2011 à 16:54
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more