170 lines
4.6 KiB
Python
170 lines
4.6 KiB
Python
# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""
|
|
|
|
Pandigital numbers in Google CP Solver.
|
|
|
|
From Albert H. Beiler 'Recreations in the Theory of Numbers',
|
|
quoted from http://www.worldofnumbers.com/ninedig1.htm
|
|
'''
|
|
Chapter VIII : Digits - and the magic of 9
|
|
|
|
The following curious table shows how to arrange the 9 digits so that
|
|
the product of 2 groups is equal to a number represented by the
|
|
remaining digits.
|
|
|
|
12 x 483 = 5796
|
|
42 x 138 = 5796
|
|
18 x 297 = 5346
|
|
27 x 198 = 5346
|
|
39 x 186 = 7254
|
|
48 x 159 = 7632
|
|
28 x 157 = 4396
|
|
4 x 1738 = 6952
|
|
4 x 1963 = 7852
|
|
'''
|
|
|
|
See also MathWorld http://mathworld.wolfram.com/PandigitalNumber.html
|
|
'''
|
|
A number is said to be pandigital if it contains each of the digits
|
|
from 0 to 9 (and whose leading digit must be nonzero). However,
|
|
'zeroless' pandigital quantities contain the digits 1 through 9.
|
|
Sometimes exclusivity is also required so that each digit is
|
|
restricted to appear exactly once.
|
|
'''
|
|
|
|
* Wikipedia http://en.wikipedia.org/wiki/Pandigital_number
|
|
|
|
|
|
Compare with the the following models:
|
|
* MiniZinc: http://www.hakank.org/minizinc/pandigital_numbers.mzn
|
|
* Comet : http://www.hakank.org/comet/pandigital_numbers.co
|
|
* ECLiPSe : http://www.hakank.org/eclipse/pandigital_numbers.ecl
|
|
* Gecode/R: http://www.hakank.org/gecoder/pandigital_numbers.rb
|
|
* ECLiPSe : http://hakank.org/eclipse/pandigital_numbers.ecl
|
|
* SICStus : http://hakank.org/sicstus/pandigital_numbers.pl
|
|
|
|
This model was created by Hakan Kjellerstrand (hakank@bonetmail.com)
|
|
Also see my other Google CP Solver models:
|
|
http://www.hakank.org/google_or_tools/
|
|
|
|
"""
|
|
from __future__ import print_function
|
|
import sys
|
|
|
|
from ortools.constraint_solver import pywrapcp
|
|
|
|
#
|
|
# converts a number (s) <-> an array of integers (t) in the specific base.
|
|
#
|
|
|
|
|
|
def toNum(solver, t, s, base):
|
|
tlen = len(t)
|
|
solver.Add(
|
|
s == solver.Sum([(base ** (tlen - i - 1)) * t[i] for i in range(tlen)]))
|
|
|
|
|
|
def main(base=10, start=1, len1=1, len2=4):
|
|
|
|
# Create the solver.
|
|
solver = pywrapcp.Solver("Pandigital numbers")
|
|
|
|
#
|
|
# data
|
|
#
|
|
max_d = base - 1
|
|
x_len = max_d + 1 - start
|
|
max_num = base ** 4 - 1
|
|
|
|
#
|
|
# declare variables
|
|
#
|
|
num1 = solver.IntVar(0, max_num, "num1")
|
|
num2 = solver.IntVar(0, max_num, "num2")
|
|
res = solver.IntVar(0, max_num, "res")
|
|
|
|
x = [solver.IntVar(start, max_d, "x[%i]" % i) for i in range(x_len)]
|
|
|
|
#
|
|
# constraints
|
|
#
|
|
solver.Add(solver.AllDifferent(x))
|
|
|
|
toNum(solver, [x[i] for i in range(len1)], num1, base)
|
|
toNum(solver, [x[i] for i in range(len1, len1 + len2)], num2, base)
|
|
toNum(solver, [x[i] for i in range(len1 + len2, x_len)], res, base)
|
|
|
|
solver.Add(num1 * num2 == res)
|
|
|
|
# no number must start with 0
|
|
solver.Add(x[0] > 0)
|
|
solver.Add(x[len1] > 0)
|
|
solver.Add(x[len1 + len2] > 0)
|
|
|
|
# symmetry breaking
|
|
solver.Add(num1 < num2)
|
|
|
|
#
|
|
# solution and search
|
|
#
|
|
solution = solver.Assignment()
|
|
solution.Add(x)
|
|
solution.Add(num1)
|
|
solution.Add(num2)
|
|
solution.Add(res)
|
|
|
|
db = solver.Phase(x,
|
|
solver.INT_VAR_SIMPLE,
|
|
solver.INT_VALUE_DEFAULT)
|
|
|
|
solver.NewSearch(db)
|
|
num_solutions = 0
|
|
solutions = []
|
|
while solver.NextSolution():
|
|
print_solution([x[i].Value() for i in range(x_len)], len1, len2, x_len)
|
|
num_solutions += 1
|
|
|
|
solver.EndSearch()
|
|
|
|
if 0 and num_solutions > 0:
|
|
print()
|
|
print("num_solutions:", num_solutions)
|
|
print("failures:", solver.Failures())
|
|
print("branches:", solver.Branches())
|
|
print("WallTime:", solver.WallTime())
|
|
print()
|
|
|
|
|
|
def print_solution(x, len1, len2, x_len):
|
|
print("".join([str(x[i]) for i in range(len1)]), "*", end=' ')
|
|
print("".join([str(x[i]) for i in range(len1, len1 + len2)]), "=", end=' ')
|
|
print("".join([str(x[i]) for i in range(len1 + len2, x_len)]))
|
|
|
|
|
|
base = 10
|
|
start = 1
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) > 1:
|
|
base = int(sys.argv[1])
|
|
if len(sys.argv) > 2:
|
|
start = int(sys.argv[2])
|
|
|
|
x_len = base - 1 + 1 - start
|
|
for len1 in range(1 + (x_len)):
|
|
for len2 in range(1 + (x_len)):
|
|
if x_len > len1 + len2:
|
|
main(base, start, len1, len2)
|