Przeplatanie rozrzedzone posortowane tablice

głosy
7

Mam zestaw wykazów wydarzeń. Zdarzenia zawsze zdarzają się w określonej kolejności, ale nie każde zdarzenie zawsze się zdarza. Oto wejście przykład:

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

Wartości wejściowe nie mają wrodzoną zamówienia. Są faktycznie komunikaty takie jak „tworzenie dowiązania” i „reindeksowania wyszukiwanie”. Są one klasyfikowane w indywidualnej liście, ale nie ma sposobu, aby spojrzeć na tylko „fa” w pierwszej listy i „mi” w drugim i określić, która jest przed drugim.

Chciałbym być w stanie przyjąć, że wejście i wygenerować posortowaną listę wszystkich zdarzeń:

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

albo jeszcze lepiej, niektóre informacje o każdym zdarzeniu, jak Count:

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

Czy jest nazwa dla tego, co robię? Są tam akceptowane algorytmy? Piszę to w Perlu, czy to ważne, ale pseudokod zrobi.

Wiem, że biorąc pod uwagę mój przykład wejście, prawdopodobnie nie może być zagwarantowane w „prawo” kolejności. Ale moje prawdziwe wejście ma ton więcej punktów danych, a jestem przekonany, że z pewnym sprytem to będzie 95% w prawo (co jest naprawdę wszystko, czego potrzebuję). Ja po prostu nie chcę wymyślać koła, jeśli nie muszę.

Utwórz 09/07/2010 o 19:32
źródło użytkownik
W innych językach...                            


10 odpowiedzi

głosy
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 skrót mogę myśleć. Tak czy inaczej, trzeba wykonać iterację rzeczy przynajmniej raz ...

Odpowiedział 09/07/2010 o 19:42
źródło użytkownik

głosy
0

Jest to idealny kandydat na sortowanie przez scalanie . Przejdź do strony wikipedia tutaj całkiem dobrej reprezentacji algorytmu http://en.wikipedia.org/wiki/Merge_sort

Co masz opisany jest faktycznie podzbiorem / mały uszczypnąć od rodzaju seryjnej. Zamiast zaczynać z niesegregowanych tablicy, masz zestaw posortowanych tablic, które chcesz połączyć ze sobą. Wystarczy wywołać funkcję „scalić”, jak opisano na stronie wikipedii na parach swoimi tablicami i wyników funkcji scalania aż masz pojedynczą tablicę (które będą sortowane).

Wykręcanie wyjście na drodze chcesz, musisz zdefiniować funkcję porównawczą, która może powrócić, jeśli jedno zdarzenie jest mniejszy, równy lub większy niż innym zdarzeniem. Wtedy, gdy funkcja scalania znajdzie dwa wydarzenia, które są równe, można zwinąć je w jednym przypadku i prowadź rachubę dla tego wydarzenia.

Odpowiedział 09/07/2010 o 19:45
źródło użytkownik

głosy
3

Teoretycznie rzecz biorąc, pozwól mi sugerują następujący algorytm:

  1. Budowanie skierowaną wykres.
  2. Dla każdego wejścia [X, Y, Z] utworzyć krawędzie X> Y> typu Y i Z, jeśli nie są one już istnieje.
  3. Przeprowadzić sortowanie topologiczne wykresu.
  4. Voila!

PS
To jest tylko przy założeniu, że wszystkie zdarzenia w określonej kolejności (zawsze!). Jeśli to nie jest przypadek, problem staje NP-zupełny.

PPS
I właśnie dlatego, że masz coś pożytecznego: Sortuj :: topologicznych (nie wiem, czy to faktycznie działa, ale wydaje się po prawej stronie)

Odpowiedział 09/07/2010 o 19:48
źródło użytkownik

głosy
0

Z grubsza, nazwa dałbym jest „mieszania”. Jesteś oddanie rzeczy do par wartości nazwa. Jeśli chcesz zachować pewne pozory porządku, trzeba uzupełnić hash z tablicą, która utrzymuje porządek. Że rozkaz jest „porządek spotkanie” dla mnie.

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

Istnieją inne odpowiedzi, jak ten jeden, ale kopalnia zawiera ten schludny autovivification podstęp.

Odpowiedział 09/07/2010 o 20:31
źródło użytkownik

głosy
2

Jeśli nie jesteś w formie pisemnej do większego kodu, można użyć narzędzia wiersza polecenia 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

Który znajduje się lista wszystkich par w danym wejściu próbki. To daje na wyjściu:

do
la
re
so
mi
fa
ti
za

która jest w zasadzie to, co chcesz.

Odpowiedział 09/07/2010 o 21:06
źródło użytkownik

głosy
3

Można użyć tsortwywnioskować rozsądne, chociaż niekoniecznie wyjątkowy porządek sortowania (znany jako kolejności topologicznej ) od porządkowania już obserwowane. Możesz być zainteresowany przeczytaniem tsort„s oryginalnego wykorzystania , który jest podobny w konstrukcji do swojego problemu.

Zauważ, że tsortwymaga acykliczny graf. Pod względem swojej przykład, oznacza to, że nie widział, nie następuje ponownie w jednej sekwencji i ponownie następuje uwagi w inną.

#! /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;
}

Ponieważ opisane dane jako rzadki, powyższy kod dostarcza tsorttak dużo informacji, jak to możliwe o przylegania macierzy zdarzeń.

Mając te informacje, obliczania histogramu i sortowania jego elementów jest bardzo proste:

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";
}

Dla wejścia w swoim pytaniu podany, wyjście jest

[Do, 3]
[La, 1]
[Re, 3]
[Więc, 1]
[Mil, 2]
[Fa 2]
[Ti 2]
[ZA, 2]

Wygląda to śmieszne, bo wiemy kolejność Solfege, ale ponownie i la są nieporównywalne w celu częściowego zdefiniowanego przez $events: wiemy tylko, że muszą one pochodzić zarówno po zrobić.

Odpowiedział 09/07/2010 o 21:22
źródło użytkownik

głosy
0

Nie jestem pewien, co by to nazwać obaj, ale wymyśliliśmy sposób na znalezienie zlecenia daną tablicę tablic jako wejście. W istocie pseudo-kod jest:

10 Znajdź najwcześniejszą pozycję we wszystkich tablicach
20 Naciśnij że na listę
30 Usuń ten element ze wszystkich tablic
40 Goto 10 Jeśli istnieją jakiekolwiek przedmioty pozostawione

Oto prototyp:

#!/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);

Wydajność:

do, re, mi, fa, la, ti, tak, za

Odpowiedział 09/07/2010 o 21:42
źródło użytkownik

głosy
0

Rozwiązanie:

To rozwiązuje oryginalne pytanie, zanim została zmodyfikowana przez pytającego.


#!/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 
      }      
   } 

Jak pobrać 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   

Zasadniczo możemy stworzyć niepowtarzalny listę elementów w hash. Dla każdego z tych elementów mamy „właściwość” hash, który zawiera skalarne counti tablicę dla position. Liczba elementów w tablicy powinien się zmieniać, na podstawie ilu wystąpień elementu były w oryginale.

Nieruchomość skalarne nie jest naprawdę konieczne, ponieważ zawsze można wziąć skalara na positiontablicy, aby pobrać ten sam numer. Uwaga: jeśli kiedykolwiek dodać / usunąć elementy z tablicy counti positionnie będzie korelować z ich znaczeniem.

  • Przykład: print scalar @{$new{za}{position}};daje takie same jakprint $new{za}{count};
Odpowiedział 09/07/2010 o 22:20
źródło użytkownik

głosy
0

Wystarczy sobie sprawę, że ich na Twoje pytanie nie jest z góry ustalony porządek, więc to nie może być dowiemy się z.

Kod 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);

wydajność:

[do 3]
[re 3]
[mi 2]
[fa 2]
[so 1]
[la 1]
[ti 2]
[za 2]
Odpowiedział 10/07/2010 o 16:32
źródło użytkownik

głosy
1

Użyj skrótu do agregacji.

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;

plony

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

Wartość% hash się łatwo przekształcić w listy, jeśli to, co chcesz.

my @newout;
push @newout,[$_,$out{$_}] foreach sort keys %out;
Odpowiedział 21/04/2011 o 16:54
źródło użytkownik

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