Lychrel numbers: Difference between revisions
Content added Content deleted
SqrtNegInf (talk | contribs) m (→{{header|Perl}}: fixed typo) |
|||
Line 2,136: | Line 2,136: | ||
<pre>{4994,8778,9999} |
<pre>{4994,8778,9999} |
||
</pre> |
</pre> |
||
=={{header|Nim}}== |
|||
It would be possible to work with big numbers thanks to some third party library, but there is actually no need for this. With big numbers, we would have to convert from Int to string and back from string to Int which is costly. It seems better to work with a sequence of digits (not chars!) and to define a function "addReverse" and a function "inc". This way, we minimize the number of conversions to strings and avoid completely the conversion from string to Int. |
|||
The program runs in 20-30 ms and there is probably still some room for optimizations. |
|||
<lang Nim>import algorithm, sequtils, strformat, strutils, tables |
|||
type |
|||
Digit = range[0..9] |
|||
Number = seq[Digit] # Number as a sequence of digits (unit at index 0). |
|||
# Structure used to find the candidates for Lychrel numbers. |
|||
Candidates = object |
|||
seeds: seq[Number] # List of possible seeds. |
|||
linked: Table[Number, Number] # Maps a number to a smallest number in a sequence. |
|||
#################################################################################################### |
|||
# Conversions. |
|||
func toInt(n: Number): Natural = |
|||
## Convert a Number to an int. No check is done for overflow. |
|||
for i in countdown(n.high, 0): |
|||
result = 10 * result + n[i] |
|||
func `$`(n: Number): string = |
|||
## Return the string representation of a Number. |
|||
reversed(n).join() |
|||
func `$`(s: seq[Number]): string = |
|||
## Return the string representation of a sequence of Numbers. |
|||
s.mapIt($it).join(" ") |
|||
#################################################################################################### |
|||
# Operations on Numbers. |
|||
func addReverse(n: var Number) = |
|||
## Add a Number to its reverse. |
|||
var carry = 0 |
|||
var high = n.high |
|||
var r = reversed(n) |
|||
for i in 0..high: |
|||
var sum = n[i] + r[i] + carry |
|||
if sum >= 10: |
|||
n[i] = sum - 10 |
|||
carry = 1 |
|||
else: |
|||
n[i] = sum |
|||
carry = 0 |
|||
if carry != 0: n.add(carry) |
|||
func inc(a: var Number) = |
|||
## Increment a Number by one. |
|||
for item in a.mitems: |
|||
let sum = item + 1 |
|||
if sum >= 10: |
|||
item = sum - 10 |
|||
else: |
|||
item = sum |
|||
return # No carry: stop adding. |
|||
a.add 1 # Add last carry. |
|||
#################################################################################################### |
|||
# Operations related to Lychrel numbers. |
|||
func isPalindromic(n: Number): bool = |
|||
## Check if a Number is palindromic. |
|||
for i in 0..(n.high div 2): |
|||
if n[i] != n[n.high - i]: return false |
|||
result = true |
|||
func isRelatedLychrel(candidates: Candidates; n: Number): bool = |
|||
## Check if a Number is a Lychrel related number. |
|||
var val = candidates.linked[n] |
|||
# Search for the first value in a list of linked values. |
|||
while val in candidates.linked: val = candidates.linked[val] |
|||
# If the first value is a seed, "n" is related to it. |
|||
result = val in candidates.seeds |
|||
func relatedCount(candidates: Candidates; maxlen: Positive): int = |
|||
## Return the number of related Lychrel numbers with length <= maxlen. |
|||
# Loop on values which are linked to a smallest value. |
|||
for n in candidates.linked.keys: |
|||
if n.len <= maxlen and candidates.isRelatedLychrel(n): inc result |
|||
func palindromicLychrel(candidates: Candidates; maxlen: Positive): seq[int] = |
|||
## Return the list of palindromic Lychrel numbers with length <= maxlen. |
|||
# Search among seed Lychrel numbers. |
|||
for n in candidates.seeds: |
|||
if n.len <= maxlen and n.isPalindromic: |
|||
result.add n.toInt |
|||
# Search among related Lychrel numbers. |
|||
for n in candidates.linked.keys: |
|||
if n.len <= maxlen and n.isPalindromic and candidates.isRelatedLychrel(n): |
|||
result.add n.toInt |
|||
# Sort the whole list. |
|||
result.sort() |
|||
proc check(candidates: var Candidates; num: Number) = |
|||
## Check if a Number is a possible Lychrel number. |
|||
if num in candidates.linked: return # Already encountered: nothing to do. |
|||
var val = num |
|||
for _ in 1..500: |
|||
val.addReverse() |
|||
if val in candidates.linked: |
|||
# Set the linked value of "num" to that of "val". |
|||
candidates.linked[num] = candidates.linked[val] |
|||
return |
|||
if val.isPalindromic(): return # Don't store palindromic values as they may be seeds. |
|||
candidates.linked[val] = num |
|||
candidates.seeds.add num |
|||
#——————————————————————————————————————————————————————————————————————————————————————————————————— |
|||
var cand: Candidates |
|||
var num: Number = @[Digit(0)] # The Number to test. |
|||
for n in 1..10_000: |
|||
inc num |
|||
cand.check(num) |
|||
echo &"Found {cand.seeds.len} candidate seed Lychrel numbers between 1 and 10000." |
|||
echo "These candidate seed Lychrel numbers are: ", cand.seeds |
|||
echo &"Found {cand.relatedCount(4)} candidate related Lychrel numbers between 1 and 10000." |
|||
echo "Palindromic candidate Lychrel numbers between 1 and 10000 are: ", cand.palindromicLychrel(4)</lang> |
|||
{{out}} |
|||
<pre>Found 5 candidate seed Lychrel numbers between 1 and 10000. |
|||
These candidate seed Lychrel numbers are: 196 879 1997 7059 9999 |
|||
Found 244 candidate related Lychrel numbers between 1 and 10000. |
|||
Palindromic candidate Lychrel numbers between 1 and 10000 are: @[4994, 8778, 9999] |
|||
=={{header|Pascal}}== |
=={{header|Pascal}}== |