2012-12-06 10 views
6

CondizioniQual è il modo più Ruby-simile di generare ogni combinazione unica di 3 interi positivi che aggiungono fino a 100

a + b + c = 100 
a,b,c positive integers or 0 

output desiderato:

[ 
    [0,0,100], 
    [0,1,99 ], 
    ... # all other permutations 
    [99,1,0 ], 
    [100,0,0] 
] 

StackOverflow dice il mio post non ha molto contesto per spiegare le sezioni del codice. Sei d'accordo? (Questo è il testo di riempimento per soddisfare le sue esigenze)

+0

Ricordarsi di selezionare alcune delle risposte – tokland

risposta

13

che avrei scritto:

(0..100).flat_map { |x| (0..100-x).map { |y| [x, y, 100-x-y] } } 
#=> [[0, 0, 100], [0, 1, 99]], ..., [99, 1, 0], [100, 0, 0]] 

nota Sito 1: questo è un classico esempio in cui la lista-comprensioni brillantezza (e ancora di più se ci fosse una condizione da qualche parte). Poiché Ruby non ha LC, dobbiamo eseguire la conversione tipica in OOP: N-1flat_map 's + map. Sarebbe fantastico avere LC in Ruby (controllare this feature request), Scala ha dimostrato che anche un puro linguaggio OOP trae molto vantaggio da questo zucchero sintattico (sebbene io possa capire la prevenzione dagli sviluppatori a causa del protocollo/metodo implicito iterabile). Su un rubino immaginario che li ha sostenuti devi scrivere:

[[x, y, 100-x-y] for x in 0..100 for y in 0..100-x] # imaginary Ruby 

Nota a margine 2: Immagina che si preferisce una soluzione meno dispendiosa di memoria (che probabilmente non è necessario l'intero array). Una soluzione pigra con Ruby 2.0 richiede solo di aggiungere un paio di [lazy][2] deleghe:

(0..100).lazy.flat_map { |x| (0..100-x).lazy.map { |y| [x, y, 100-x-y] } } 

Nota a margine 3: Solo per completezza, in linea di risposta @akuhn, un'altra soluzione pigra utilizzando enumeratori:

Enumerator.new do |e| 
    (0..100).each { |x| (0..100-x).each { |y| e.yield([x, y, 100-x-y]) } } 
end 
+0

non un cattivo approccio, ma avevo appena generare tutte le possibili combinazioni di a, b, c e fare un 'find_all' di combinazioni che aggiungono fino a 100. –

+2

@AndrewGrimm tua l'approccio farà circa 500.000 volte più lavoro di tokland. – dbenhur

+0

@dbenhur sì, ma quello è il lavoro che Ruby farebbe, non il lavoro che farei! –

4

Ecco la mia soluzione

for a in 0..100; for b in 0..100-a; p [a,b,100-a-b]; end; end 

che trovo si legge quasi come @ di tokland di lista. Se si desidera utilizzare i valori più a valle anziché stampa, inserire il codice in un metodo generatore

def generate 
    return enum_for(:generate) unless block_given? 
    for a in 0..100 
    for b in 0..100-a 
     yield [a,b,100-a-b] 
    end 
    end 
end 

che viene quindi utilizzato come

generate { |a,b,c| puts [a,b,c] } 

o come nel

generate { |each| p each } 

o come

p generate.to_a 

che tutti stampano tutte le tuple generate.

+0

A volte uso questo approccio tipo Python-generator anche se sembra meno funzionale (senso FP). Comunque, invece di inserire un 'to_enum' guardia (che i metodi core di Ruby fanno per essere più versatili), puoi anche decidere di restituire sempre un enumeratore, il che ha senso per output potenzialmente grandi:' Enumerator.new {| yielder | ... yielder.yield x} '. – tokland

+0

@tokland qual è la differenza tra 'to_enum' e un enumeratore? Solo una questione di stile o c'è una differenza di implementazione? Immagino che entrambi usino le fibre per le coroutine. – akuhn

+0

@tokland la domanda riguardava "stile Ruby" e non uno "stile di programmazione funzionale" :) – akuhn

3

direi modo più rubino-ish (che non è affatto il modo più efficiente) è presente:

(0..100).to_a.repeated_permutation(3).find_all{|triplet|triplet.inject(:+)==100} 

L'unica parte non trasparente di questa catena è il to_a, che converte l'oggetto intervallo dato da (0..100) in un array (gli intervalli sono pigri, il che non funziona con repeated_permutation).

+0

Mi piace! Sebbene sembri la maggior parte del Ruby-ish (vale a dire magia, terse e linguaggio naturale-ish), accetterò la risposta di @ tokland, in questo caso. Sono aperto alla discussione, però. –

+0

Senza dubbio questo è più semplice da capire (non direi altro Ruby-sh, perché è così?), E se avessi saputo che N fosse sempre piccolo, probabilmente lo farei. Tuttavia, da un punto di vista computazionale la conversione di un problema O (n^2) in una O (n^3) è un po 'dubbia a dir poco. Aumenta un po N e ti troverai nei guai. – tokland

+1

Questo è bello, ma non trova tutte le combinazioni. Include '[0,0,100]', ma non '[100,0,0]', per esempio. – Adam