Intercalarea matrice sortate rare

voturi
7

Am un set de liste de evenimente. Evenimentele se întâmplă întotdeauna într-o anumită ordine, dar nu fiecare eveniment se întâmplă întotdeauna. Iată un exemplu de intrare:

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

Valorile de intrare nu au nici o ordine inerentă. Sunt de fapt mesaje cum ar fi „crearea de legături simbolice“ și „Reindexare căutarea“. Ei sunt sortate în lista individuale, dar nu există nici o modalitate de a privi numai „fa“ din prima listă și „mi“ în a doua și a determina care vine înaintea celuilalt.

Aș vrea să fie în măsură să ia această intrare și de a genera o listă sortată a tuturor evenimentelor:

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

sau mai bine încă, unele informații despre fiecare eveniment, cum ar fi un număr de:

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

Există un nume pentru ceea ce fac? Există algoritmi acceptat? Scriu acest lucru în Perl, în cazul în care contează, dar pseudocod va face.

Știu că exemplul dat de intrare meu, eu , probabil , nu poate fi garantată de ordinul „dreapta“. Dar intrarea mea reală are de tone mai mult datapoints, și mă simt încrezător că , cu unele ingeniozitate va fi de 95% din dreapta (care este într - adevăr tot ce am nevoie). Pur și simplu nu vreau să re-inventeze roata dacă nu trebuie.

Întrebat 09/07/2010 la 19:32
sursa de către utilizator
În alte limbi...                            


10 răspunsuri

voturi
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 scurtătură mă pot gândi. Oricum, trebuie să itera prin lucruri cel puțin o dată ...

Publicat 09/07/2010 la 19:42
sursa de către utilizator

voturi
0

Acesta este un candidat perfect pentru un Merge Sort . Du - te la pagina de wikipedia aici pentru o reprezentare destul de bună a algoritmului http://en.wikipedia.org/wiki/Merge_sort

Ceea ce am descris este de fapt un subset / tweak mic de genul îmbinare. În loc de a începe cu o serie nesortate, aveți un set de matrice sortate pe care doriți să le îmbinați împreună. Apelează funcția „îmbinare“, așa cum este descris în pagina wikipedia pe perechi de matricile și rezultatele funcției de îmbinare până când aveți o singură matrice (care vor fi sortate).

Pentru a optimiza ieșirea la modul în care doriți, veți avea nevoie pentru a defini o funcție comparație care poate reveni în cazul în care un eveniment este mai mică, egală sau mai mare decât un eveniment diferit. Apoi, atunci când funcția de îmbinare găsește două evenimente, care sunt egale, le puteți prăbuși într-un singur eveniment și să păstreze un număr pentru acel eveniment.

Publicat 09/07/2010 la 19:45
sursa de către utilizator

voturi
3

Teoretic vorbind, să-mi sugereze următorul algoritm:

  1. Construiți un grafic direcționat.
  2. Pentru fiecare intrare [X, Y, Z], de a crea marginile X-> Y și Z> Y- în cazul în care nu sunt deja acolo.
  3. Efectuați o sortare topologică a graficului.
  4. Voila!

PS
Acest lucru este presupunând doar că toate evenimentele au loc într - o anumită ordine (întotdeauna!). În cazul în care nu este cazul, problema devine NP-completă.

PPS
Si doar pentru ca ai ceva util: Sortare :: topologică (nu știu dacă funcționează de fapt , dar se pare corect)

Publicat 09/07/2010 la 19:48
sursa de către utilizator

voturi
0

Aproximativ, numele mi-ar da este „hashing“. Tu pun lucrurile în valoare de perechi nume. Dacă doriți să păstrați o anumită aparență de ordine, trebuie să completeze hash cu o matrice care ține ordine. Această ordine este „ordine întâlnire“ pentru mine.

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

Există și alte răspunsuri de genul acesta, dar a mea conține acest truc autovivification elegant.

Publicat 09/07/2010 la 20:31
sursa de către utilizator

voturi
2

Dacă nu sunteți în scris de mult cod, ați putea utiliza utilitarul unix linie de comandă tsort:

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

Ceea ce este o listă a tuturor perechilor din datele introduse de probă. Acest lucru produce ca ieșire:

do
la
re
so
mi
fa
ti
za

care este de fapt ceea ce vrei.

Publicat 09/07/2010 la 21:06
sursa de către utilizator

voturi
3

Puteți utiliza tsortpentru a deduce un ordin nu neapărat unic sortare rezonabilă , deși (cunoscut ca un ordin topologic ) de la comandarea care le - ați observat. Ați putea fi interesat de lectură tsorte utilizarea originală , care este o structură similară cu problema ta.

Rețineți că tsortnecesită un grafic aciclic. În ceea ce privește exemplul dvs., acest lucru înseamnă că nu a putut vedea nu urmată de re într - o singură secvență și re urmată de do în alta.

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

Pentru că ați descris ca datele rare, codul de mai sus oferă tsortinformații cât mai mult posibil despre matricea de adiacență evenimentelor.

Având aceste informații, calcul o histogramă și sortarea componentelor sale este simplă:

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

Pentru intrarea în întrebarea dumneavoastră ați furnizat, de ieșire este

[Do, 3]
[La, 1]
[Re, 3]
[Deci, 1]
[Mi, 2]
[Fa, 2]
[Ti, 2]
[Za, 2]

Acest lucru pare amuzant pentru că știm ordinea solfege, dar re și la sunt incomparabile în ordinea parțială definită prin $events: știm doar că ei trebuie să vină amândoi după facă.

Publicat 09/07/2010 la 21:22
sursa de către utilizator

voturi
0

Nu sunt foarte sigur ce acest lucru ar fi numit, fie, dar am dat seama o modalitate de a găsi ordinea dată matrice de matrice ca o intrare. În esență, pseudo-cod este:

10 Găsiți mai devreme element în toate matricele
20 Push că pe o listă de
30 Eliminați elementul din toate matricele
40 Du - te la 10 în cazul în care există elemente rămase

Iată un prototip de lucru:

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

ieşire:

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

Publicat 09/07/2010 la 21:42
sursa de către utilizator

voturi
0

Soluţie:

Aceasta rezolvă întrebarea inițială înainte de a fi fost modificat de Autorul întrebării.


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

Cum de a prelua informații:

   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   

Practic, vom crea o listă unică de elemente hash. Pentru fiecare dintre aceste elemente avem o „proprietate“ hash, care conține un scalar countși o matrice pentru position. Numărul de elemente din matrice ar trebui să varieze, în funcție de cât de multe apariții ale elementului au fost în original.

Proprietatea scalară nu este cu adevărat necesar , deoarece ai putea lua întotdeauna scalar de positionmatrice pentru a prelua același număr. Notă: dacă adăugați vreodată / elimina elemente din matrice countși positionnu vor fi coreleze în sensul lor.

  • exemplu: print scalar @{$new{za}{position}};vă va da la fel caprint $new{za}{count};
Publicat 09/07/2010 la 22:20
sursa de către utilizator

voturi
0

Doar dat seama întrebarea ta a spus lor este nici o ordine prestabilită, astfel încât aceasta să nu fie sunt relevante.

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

ieșire:

[do 3]
[re 3]
[mi 2]
[fa 2]
[so 1]
[la 1]
[ti 2]
[za 2]
Publicat 10/07/2010 la 16:32
sursa de către utilizator

voturi
1

Utilizați un hash pentru agregare.

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;

Yield-urile

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

% Din hash este ușor de transformat într-o listă în cazul în care este ceea ce vrei.

my @newout;
push @newout,[$_,$out{$_}] foreach sort keys %out;
Publicat 21/04/2011 la 16:54
sursa de către utilizator

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