[Tutor] Testing if a number occurs more than once
Michael Williams
michael.williams@st-annes.oxford.ac.uk
Tue Dec 3 12:47:03 2002
Well, the general consensus is that there isn't much general consensus
and that the best solution depends on the form of the problem.
The problem is a trivial little thing from the British magazine ``New
Scientist''. Each week, in a section called ``Enigma'' they set a
number puzzle. I could see no way of solving this week's analytically,
so threw Python at it. Here's the problem:
New Scientist wrote:
> Brits who venture to Euroland quickly discover that you get
> one-and-a-bit Euros for every pound. I can reveal that the "bit"
> varies between 4/3 and 11/8 because:
> EURO * 4/3 == POUND
> EURO * 11/8 == POUND
> These two sums are entirely distinct: any letter may or may not have
> the same value in one sum as in the other. But within each sum digits
> have been consistently represented by capital letters, different
> letters being used for different digits. No number starts with a zero.
> What were the five-digit numbers represented by pound in (a) and (b)?
So it's the same problem with two different coefficients. Let's
consider (b). Firstly, we know the minimum possible value of POUND is
10000 since neither number starts with a zero. Therefore, the minimum
EURO is 10000/(11/8) == 7272. This saves some time during the program.
So here is the first solution I came up with:
========================================
coeff = 1 + 3.0/8
min_euro = 10000/coeff
solved = 0
for euro in range(min_euro, 10000, 1):
# go through the possible EUROs.
e = int(str(euro)[0])
u = int(str(euro)[1])
r = int(str(euro)[2])
o = int(str(euro)[3])
# go through the possible POUNDs (O and U already decided)
for p in range(1,10,1):
for n in range(0,10,1):
for d in range(0,10,1):
pound_s = str(p) + str(o) + str(u) + str(n) + str(d)
pound = int(pound_s)
numbers = [int(str(euro)[0]), int(str(euro)[1]),
int(str(euro)[2]), int(str(euro)[3]),
int(str(pound)[0]), int(str(pound)[3]),
int(str(pound)[4])]
if not morethanone(numbers):
if test(euro, pound, coeff):
solved = 1
break
if solved == 1: break
if solved == 1: break
if solved == 1: break
================================
test() is just a function that tests whether euro * coeff == pound.
morethanone() is the function that determines whether or not an number
occurs more than once in the list numbers. So morethanone() acts on a
list only seven elements long, which is going to affect the way we
write morethanone().
Having thought about it some more I realised that where I call
morethanone() will drastically affect how much effort I put into
optimising it. In the above form morethanone is called for *every*
combination of the seven digits in the range. That's a lot of times.
By calling it only after establishing that euro + coeff == pound, we
reduce the number of calls it gets to half-a-dozen times. This is
probably the best way to do it: call test() many times, call
morethanone() few, since test() is simple, morethanone() is not.
So, with a bit of thought on my part, I realised that the optimisation
of morethanone is a red-herring in this case and profiling the various
morethanone()s (summarised below) in this case would not throw up any
significiant differences.
Here are the morethanone()s suggested:
Yann Le Du:
-----------
for n in l:
if l.count(n) > 1:
return 1
return 0
Jeff Shannon (and a similar one from Danny Yoo):
------------------------------------------------
mycopy = l[:] # as Jeff suggested this is unnecessary in this case
mycopy.sort()
for n in range(1, len(mycopy))
if mycopy[n] == mycopy[n-1]:
return 1
return 0
Jeff Shannon (and a similar one--I think!--from Alan Gauld)
-----------------------------------------------------------
res = {}
for item in l:
if res.get(item, 0):
res[item] = 1
else:
return 1
return 0
p.s. If anyone does the problem themselves, I make the answers:
in (a) POUND is 12056
in (b) it is 10945
--
Michael