Zählen Sie eine Teilmenge eines in Ruby angegebenen Arrays auf (+ α).

Bei der Lösung wettbewerbsfähiger Programmierprobleme musste ich manchmal ** Zustände für jeden Fall erstellen und verarbeiten, anstatt nur die Anzahl der Fälle zu zählen. In Ruby Array # Kombination und [ Array # wiederholte_permutation](https: // docs.ruby-lang.org/ja/latest/method/Array/i/repeated_permutation.html) usw. sind verfügbar, aber wenn ich etwas anderes möchte, kann ich es nicht schnell zusammenbauen und es braucht Zeit.

Also werde ich versuchen, die Methode, die ich damals wollte, auch als Übung zu machen.

Teilmenge

Es gibt Zeiten, in denen Sie ** Muster abdecken möchten, die für jedes Element enthalten / nicht enthalten sind, z. B. wenn Sie sie alle gleichzeitig lösen möchten. Die Anzahl der Fälle beträgt "2 ** ary.size".

Verwenden Sie "Array # Kombination"

Die Anzahl der Elemente kann von "0" bis "ary.size" reichen, sodass Sie die Kombinationen für jedes Element auflisten können. Es ist einfach und schnell, da ** der größte Teil der Arbeit der internen Implementierung von Ruby ** überlassen bleibt.

each_subset.rb


def each_subset(ary, &block)
	0.upto(ary.size) { |n| ary.combination(n, &block) }
end

if $0 == __FILE__
	each_subset([*0..2]) { |subset| p subset }
end

output


[]
[0]
[1]
[2]
[0, 1]
[0, 2]
[1, 2]
[0, 1, 2]

Verwenden Sie die binäre Notation

Versuchen wir, die Reihenfolge der Aufzählung k (beginnend mit 0) in binärer Notation festzulegen, dass 1`` 0 direkt dem Einschluss / Ausschluss jedes Elements entspricht.

Wenn Sie es gehorsam tun, sieht es so aus. Ruby erhält den Wert des i-Bits mit Integer # [] es kann.

each_subset.rb


def each_subset(ary, &block)
	(1 << ary.size).times do |k|
		subset = ary.select.with_index { |_, i| k[i] == 1 }
		block.call(subset)
	end
end

if $0 == __FILE__
	each_subset([*0..2]) { |subset| p subset }
end

output


[]
[0]
[1]
[0, 1]
[2]
[0, 2]
[1, 2]
[0, 1, 2]

Da das erste Element "0" von "ary" dem niedrigsten Bit (= gerade / ungerade) entspricht, wird das Vorhandensein / Fehlen jedes Mal umgeschaltet, während das letzte Element "2" dem höchstwertigen Bit entspricht, so dass es nur in der zweiten Hälfte eingefroren wird Es erscheint.

Wie Sie sehen können, ist die Implementierung eine Doppelschleife mit einer festen Anzahl von Malen, und der Zeitaufwand für die Berechnung beträgt O (2 N). 2 Von N </ sup> ist N keine große Sache, daher sollte es im praktischen Gebrauch kein Problem geben (die Anzahl der Elemente beträgt ungefähr 16). Wenn überhaupt, mache ich mir mehr Sorgen darüber, dass ich nur mit "# select" auswerte.


Versuchen Sie vorerst O (2 N). Implementieren Sie es rekursiv und fügen Sie es in den Prozess ein, wenn es weniger "ary" gibt.

each_subset.rb


def each_subset(ary, selected = [], &block)
	if ary.empty?
		block.call(selected.dup)  #Duplizieren, da es zu gefährlich ist, wenn das Array dasselbe Objekt ist
		return
	end

	ary = ary.dup  #Duplizieren Sie, um die Eingabesequenz nicht zu unterbrechen

	elem = ary.pop
	each_subset(ary, selected, &block)  #schließt elem nicht ein
	selected.unshift(elem)
	each_subset(ary, selected, &block)  #Einschließlich elem
	selected.shift
	# ary << elem  #Nicht erforderlich, wenn dupliziert

	return  #Gibt vorerst null zurück
end

if $0 == __FILE__
	each_subset([*0..2]) { |subset| p subset }
end

<!--

Nicht rekursive Version und zeichnet intern alle Muster in einem Array auf, bevor sie ausgeführt werden (weil es einfach ist).

each_subset.rb


def each_subset(ary, &block)
	subsets = [[]]

	ary.each do |elem|
		subsets.concat(subsets.map { |ss| [*ss, elem] })
	end

	return subsets.each(&block)
end

if $0 == __FILE__
	each_subset([*0..2]) { |subset| p subset }
end

-->

Teilt

Für ary = [0, 1, 2, 3, 4] möchte ich zum Beispiel [[0, 1], [2], [3, 4]] erstellen.

Unter einem anderen Gesichtspunkt beträgt die Anzahl der Fälle "2 ** (ary.size -1)", da dies bedeutet, dass "ein Trennzeichen für ** zwischen ** Elementen des Arrays eingefügt / nicht eingefügt wird". Da es ein Rätsel ist, was zurückgegeben werden soll, wenn es sich um ein leeres Array handelt, sollten Sie es als praktisches Formular für die Implementierung verwenden.

Dies wird auch rekursiv implementiert. Wenn Sie einige gruppieren und aus "ary" extrahieren, können Sie eine weitere Gruppe mit den verbleibenden erstellen.

each_split.rb


def each_split(ary, selected = [], &block)
	if ary.empty?
		block.call(selected.map(&:dup))  #Duplizieren, da es zu gefährlich ist, wenn das Array dasselbe Objekt ist
		return
	end

	# ary == part1 +Bedienen Sie, um Teil2 zu behalten
	part1 = []
	part2 = ary.dup  #Duplizieren Sie, um die Eingabesequenz nicht zu unterbrechen

	selected << part1
	until part2.empty?
		part1 << part2.shift  #Verschieben Sie die geteilte Position nach rechts
		each_split(part2, selected, &block)
	end
	selected.pop

	# ary.replace(part1)  #Nicht erforderlich, wenn dupliziert

	return  #Gibt vorerst null zurück
end

if $0 == __FILE__
	each_split([*0..4]) { |sp| p sp }
end

output


[[0], [1], [2], [3], [4]]
[[0], [1], [2], [3, 4]]
[[0], [1], [2, 3], [4]]
[[0], [1], [2, 3, 4]]
[[0], [1, 2], [3], [4]]
[[0], [1, 2], [3, 4]]
[[0], [1, 2, 3], [4]]
[[0], [1, 2, 3, 4]]
[[0, 1], [2], [3], [4]]
[[0, 1], [2], [3, 4]]
[[0, 1], [2, 3], [4]]
[[0, 1], [2, 3, 4]]
[[0, 1, 2], [3], [4]]
[[0, 1, 2], [3, 4]]
[[0, 1, 2, 3], [4]]
[[0, 1, 2, 3, 4]]

Recommended Posts

Zählen Sie eine Teilmenge eines in Ruby angegebenen Arrays auf (+ α).
[Ruby] Die Rolle von Indizes beim Lernen von Elementen in Arrays
Verzeichnisinformationen von DEFAULT_CERT_FILE in Mac Ruby 2.0.0
Zusammenfassung der Hashes und Symbole in Ruby
[Ruby] Schleifenunterscheidung und Verwendung in Ruby
[Ruby] Über das Verhalten der Auswertung von bedingten Ausdrücken in while
[Verständnis] Unterschied zwischen Hash und Array in Ruby
Methodenname der Methodenkette in Java Builder + α
Grundlagen von Ruby
Über Ruby-Arrays
Schwer in Rubin! ??
Rufen Sie die URL des HTTP-Umleitungsziels in Ruby ab
Umgang mit Datum und Uhrzeit in Ruby. Verwenden Sie Datum und Uhrzeit richtig.