Résolution de calculs masqués (recréation Ruby / Python)

Au début

Veuillez d'abord lire ce qui suit.

$ ruby alphametics.rb 'SEND + MORE == MONEY'
SEND + MORE == MONEY
9567 + 1085 == 10652
$ ruby alphametics.rb 'DO + YOU + FEEL == LUCKY'
DO + YOU + FEEL == LUCKY
57 + 870 + 9441 == 10368

Aucune explication n'est nécessaire pour ceux qui la connaissent. Le calcul masqué est un puzzle qui est résolu en remplaçant les alphabets par des nombres de cette manière. Voici une règle rapide.

Ce solveur masqué utilise == au lieu de = pour l'égalité (la raison sera expliquée plus tard). Vous pouvez utiliser non seulement l'addition mais aussi l'addition, la soustraction, la multiplication, la division et la puissance de «**». S'il existe plusieurs solutions, la première trouvée s'affiche.

$ ruby alphametics.rb 'NORTH / SOUTH == EAST / WEST'
NORTH / SOUTH == EAST / WEST
51304 / 61904 == 7260 / 8760
$ ruby alphametics.rb 'PI * R ** 2 == AREA'
PI * R ** 2 == AREA
96 * 7 ** 2 == 4704

La division utilise la bibliothèque mathn pour effectuer des calculs fractionnaires stricts.

Ceci est basé sur la version Python 3 du solveur de calcul masqué dans Dive Into Python 3 Chapter 8, et je pense que c'est assez intéressant. J'ai fait une version Ruby. Il existe un référentiel sur github.

https://github.com/higuma/ruby-alphametics-solver

Le référentiel d'origine est le suivant.

https://github.com/diveintomark/diveintopython3

Il a plus de matériel source et est basé sur la version Python 2 suivante. Cette version ne se termine pas avec la première solution trouvée et recherche toutes les solutions.

La version Ruby n'a qu'une seule différence par rapport à la version Python, et le premier caractère 0 n'est autorisé que lorsqu'il est seul. L'exemple suivant n'a pas de solution pour la version Python, mais a une solution pour la version Ruby.

$ ruby alphametics.rb 'MALCOLM + X == MALCOLM'
MALCOLM + X == MALCOLM
1234531 + 0 == 1234531

Programme source

La version Ruby du solveur masqué est la suivante.

alphametics.rb


require 'set'
require 'mathn'

module Alphametics
  def solve(puzzle)
    puzzle = puzzle.upcase
    words = puzzle.scan /[A-Z]+/
    chars = Set.new words.join.each_char
    abort 'Too many letters' if chars.size > 10
    first_chars = Set.new words.select {|w| w.size > 1 }.map {|w| w[0] }
    n = first_chars.size
    sorted_chars = first_chars.to_a.join + (chars - first_chars).to_a.join
    %w[0 1 2 3 4 5 6 7 8 9].permutation(chars.size).each do |guess|
      next if guess[0, n].member? '0'
      expr = puzzle.tr sorted_chars, guess.join
      return expr if eval expr
    end
    return
  end
end

if __FILE__ == $PROGRAM_NAME
  include Alphametics
  ARGV.each do |arg|
    puts arg
    solution = solve arg
    puts solution if solution
  end
end

Le programme source de Python 3 est sur github.

https://github.com/diveintomark/diveintopython3/blob/master/examples/alphametics.py

Si vous dites "Read Dive In Python 3", l'explication se termine, mais ce n'est pas convivial, donc je vais vous donner le point. Le code correspond presque à 1: 1 à la version Python, je vais donc me concentrer sur les différences dans les fonctions utilisées.

La version originale de Python2 utilise string.maketrans de Python2 pour générer la table de substitution à passer à l'argument translate. Python 3 a la même fonctionnalité, str.maketrans, qui est de plus en plus rapide dans le code.

Cependant, Dive Into Python 3 n'utilise pas maketrans, mais génère d'abord un tableau de codes de caractères caractères et le traite avec dict (zip (caractères, quess)). Bien sûr, l'auteur ne doit pas ignorer les maketrans, et je pense qu'il le fait dans le but d'expliquer le fonctionnement interne.

Ruby, d'autre part, a un tr (dérivé de Perl), qui est plus facile à écrire que Python.

Précautions de sécurité

Il utilise «eval» au cœur, c'est donc une faille de sécurité lorsqu'il est utilisé dans les applications Web (cela est également mentionné dans Dive Into Python 3). Veuillez l'utiliser uniquement pour les loisirs dans l'environnement local.

Vous pouvez facilement trouver la version Web du solveur de calcul masqué en la recherchant. J'en ai trouvé un fabriqué au Japon, je vais donc le présenter.

http://bach.istc.kobe-u.ac.jp/llp/crypt-jp.html

Cependant, d'après ce que j'ai étudié, tous les sites ne prennent en charge que le calcul masqué de l'addition. C'est aussi probablement pour des raisons de sécurité.

référence

Le référentiel Dive Into Python 3 contient du code de test simple. J'ai donc créé une version Ruby de la même fonctionnalité et l'ai exécutée à la place du benchmark pour comparer les temps d'exécution (le README dans le référentiel github a la sortie complète du test).

Temps d'exécution(Secondes)
Python3 5.134
Ruby 1.9.3 2.551
Ruby 2.0.0 3.839

Dans l'ensemble, la version Ruby a tendance à être un peu plus rapide. Voici un autre exemple de mesure.

$ time python3 alphametics.py 'EARTH + AIR + FIRE + WATER == NATURE'
EARTH + AIR + FIRE + WATER == NATURE
67432 + 704 + 8046 + 97364 == 173546

real    0m35.305s
user    0m35.194s
sys     0m0.024s
$ rbenv local 1.9.3-p448        #changement de version de ruby
$ time ruby alphametics.rb 'EARTH + AIR + FIRE + WATER == NATURE'
EARTH + AIR + FIRE + WATER == NATURE
67432 + 704 + 8046 + 97364 == 173546

real    0m20.051s
user    0m19.921s
sys     0m0.040s
$ rbenv local 2.0.0-p247        #changement de version de ruby
$ time ruby alphametics.rb 'EARTH + AIR + FIRE + WATER == NATURE'
EARTH + AIR + FIRE + WATER == NATURE
67432 + 704 + 8046 + 97364 == 173546

real    0m24.925s
user    0m24.834s
sys     0m0.032s

J'ai essayé quelques autres choses, mais la version Ruby a clairement tendance à être 1,5 à 2 fois plus rapide que la version Python 3.

La version Python3 fait la table de remplacement avec dict (zip (...)) au lieu de maketrans. J'ai pensé que cela pourrait en être la cause, alors j'ai changé la version Python3 et créé une source qui utilise maketrans et l'ai essayé. Cependant, mesuré et comparé, l'effet d'amélioration était d'environ 2-3%, ce qui n'était pas autant que je m'y attendais. Il semble que Ruby soit plus rapide pour ce type de traitement.

Recommended Posts

Résolution de calculs masqués (recréation Ruby / Python)
Opération d'ensemble Python
Opération d'ensemble Python
Ruby, Python et carte
Python et Ruby se séparent
Résoudre le calcul du masque
Résolution avec Ruby et Python AtCoder ARC 059 C Méthode du carré minimum
Résolution avec Ruby et Python AtCoder ABC178 D Méthode de planification dynamique
Résolution avec Ruby et Python AtCoder ABC151 D Recherche de priorité de largeur
Résolution avec Ruby et Python AtCoder AISING2020 D Méthode carrée itérative
Résolution avec Ruby, Perl, Java et Python AtCoder ATC 002 A
Résolution avec Ruby et Python AtCoder ABC011 C Méthode de planification dynamique
Résolution avec Ruby et Python AtCoder ABC153 E Méthode de planification dynamique
Résolution avec Ruby, Perl, Java et Python AtCoder ATC 002 B
Résolution avec Ruby et Python AtCoder ABC138 D Liste adjacente
Python sur Ruby et Ruby en colère sur Python
Quatre règles de python
Java VS PHP VS Python VS Ruby
Résoudre les mathématiques avec Python (incomplet)
Mémo tranche python et rubis
Entrée standard / résumé / python, ruby
Zundokokiyoshi avec python / rubis / Lua
À propos de Perl, Python, PHP, Ruby
Résolution de Nampre avec Python (partie 2)
Syntaxe Ruby et Python ~ branch ~
Ruby, Guide d'installation du module Python
Résolution avec Ruby, Python et numpy AtCoder ABC054 B Calcul de la matrice
Résolution avec Ruby, Python et networkx AtCoder ABC168 D Liste adjacente
Résolution avec Ruby, Perl, Java et Python AtCoder ABC 065 C-th power