Énumérer un sous-ensemble d'un tableau donné en Ruby (+ α)

Lors de la résolution de problèmes de programmation compétitifs, j'avais parfois besoin de ** créer et traiter des états pour chaque cas, plutôt que de simplement compter le nombre de cas. Dans Ruby, [ʻArray # combination](https://docs.ruby-lang.org/ja/latest/method/Array/i/combination.html) et [ʻArray # repeat_permutation](https: // docs.ruby-lang.org/ja/latest/method/Array/i/repeated_permutation.html) etc. sont disponibles, mais quand je veux autre chose, je ne peux pas l'assembler rapidement et cela prend du temps.

Je vais donc essayer de faire la méthode que je voulais à ce moment-là, également en tant que pratique.

Sous-ensemble

Il y a des moments où vous voulez ** couvrir des modèles qui sont inclus / non inclus pour chaque élément, par exemple lorsque vous voulez les résoudre tous en même temps. Le nombre de cas est "2 ** ary.size".

Utilisez ʻArray # combination`

Le nombre d'éléments peut être de «0» à «ary.size », vous pouvez donc lister les combinaisons pour chacun. C'est facile et ** c'est rapide car l'essentiel du traitement est laissé à l'implémentation interne de Ruby **.

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]

Utiliser la notation binaire

Essayons de faire l'ordre d'énumération «k» (à partir de 0) en notation binaire que «1» «0» correspond à l'inclusion / exclusion de chaque élément tel quel.

Si vous le faites docilement, cela ressemble à ceci. Ruby obtient la valeur du bit ʻiavec [ʻInteger # []](https://docs.ruby-lang.org/ja/latest/method/Integer/i/=5b=5d.html) ça peut.

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]

Puisque le premier élément «0» de «ary »correspond au bit le moins significatif (= pair / impair), la présence / absence est commutée à chaque fois, tandis que le dernier élément« 2 »correspond au bit le plus significatif, il n'est donc figé que dans la seconde moitié. Il apparaît.

Comme vous pouvez le voir, l'implémentation est une double boucle avec un nombre fixe de fois, et le temps de calcul est O (2 N </ sup> N). 2 A partir de N </ sup>, N n'est pas un gros problème, il ne devrait donc pas y avoir de problème d'utilisation pratique (le nombre d'éléments est d'environ 16). Si quoi que ce soit, je suis plus préoccupé par le fait que j'évalue simplement avec # select.


Essayez O (2 N </ sup>) pour le moment. Implémentez-le récursivement et déposez-le dans le processus quand il y en a un de moins.

each_subset.rb


def each_subset(ary, selected = [], &block)
	if ary.empty?
		block.call(selected.dup)  #Dupliquer car c'est trop dangereux si le tableau est le même objet
		return
	end

	ary = ary.dup  #Dupliquer pour ne pas interrompre la séquence d'entrée

	elem = ary.pop
	each_subset(ary, selected, &block)  #n'inclut pas d'élem
	selected.unshift(elem)
	each_subset(ary, selected, &block)  #Y compris elem
	selected.shift
	# ary << elem  #Non requis si dupliqué

	return  #Renvoie nul pour le moment
end

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

<!--

Version non récursive, et enregistre en interne tous les modèles dans un tableau avant de s'exécuter (car c'est facile).

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

-->

Divisé

Pour ʻary = [0, 1, 2, 3, 4] , je veux créer, par exemple, [[0, 1], [2], [3, 4]] `.

D'un point de vue différent, le nombre de cas est 2 ** (ary.size -1) car cela signifie qu '"un délimiteur est inséré / non inséré pour ** entre ** éléments du tableau". Puisqu'il s'agit d'un mystère de ce qu'il faut renvoyer lorsqu'il s'agit d'un tableau vide, faites-en une forme pratique pour l'implémentation.

Ceci est également implémenté de manière récursive. Si vous groupez et extrayez certains de ʻary`, vous pouvez créer un autre groupe avec les autres.

each_split.rb


def each_split(ary, selected = [], &block)
	if ary.empty?
		block.call(selected.map(&:dup))  #Dupliquer car c'est trop dangereux si le tableau est le même objet
		return
	end

	# ary == part1 +Opérer pour garder part2
	part1 = []
	part2 = ary.dup  #Dupliquer pour ne pas interrompre la séquence d'entrée

	selected << part1
	until part2.empty?
		part1 << part2.shift  #Déplacer la position de division vers la droite
		each_split(part2, selected, &block)
	end
	selected.pop

	# ary.replace(part1)  #Non requis si dupliqué

	return  #Renvoie nul pour le moment
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

Énumérer un sous-ensemble d'un tableau donné en Ruby (+ α)
[Ruby] Le rôle des indices dans l'apprentissage des éléments dans les tableaux
Informations de répertoire de DEFAULT_CERT_FILE dans Mac ruby 2.0.0
Résumé des hachages et symboles dans Ruby
[Ruby] Distinction et utilisation des boucles dans Ruby
[Ruby] À propos du comportement d'évaluation des expressions conditionnelles dans while
[Comprendre] Différence entre le hachage et le tableau dans Ruby
Nom de méthode de la chaîne de méthodes dans Java Builder + α
Bases de Ruby
À propos des tableaux Ruby
Lourd en rubis! ??
Obtenez l'URL de la destination de la redirection HTTP dans Ruby
Traitement de la date et de l'heure en Ruby. Utilisez correctement la date et l'heure.