RetroBASIC
Basicprogramming(.org) => Code and examples => Topic started by: B+ on January 14, 2016, 01:53:47 AM
-
In Just Basic, the most recent discussion has been about Lychrel numbers. Considering the way Lychrel numbers are determined, I thought it amusing to find Palindromes that turn out to be Lychrel like in 50 rounds of sums of a number and a reverse. (Lychrel numbers never produce a palindrome number but here we are satisfied it is Lychrel like if no palindromes show up in 50 rounds.) So maybe you can see the irony of starting with palindrome numbers.
'palindrome Lychrel numbers for JB 2016-01-13 B+=MGA
print "Palindrome Lychrel(-like in 50 rounds) numbers:"
global pcount
while pcount<100
i=i+1
scan
if isPalindrome(i) then
if is50Lychrel(i) then call show i
end if
wend
print:print "Palindrome Lychrel count is ";pcount
sub show pal
print using("#######",pal);
pcount=pcount+1
if pcount mod 10 =0 then print
end sub
function reverse(num)
snum$=str$(num)
for I=1 to len(snum$)
p$=mid$(snum$,I,1)+p$
next
reverse=val(p$)
end function
function is50Lychrel(num)
is50Lychrel=0
t$=str$(num+reverse(num))
for j=1 to 50
p$=str$(reverse(val(t$)))
if p$=t$ then exit function else t$=str$(val(t$)+val(p$))
next j
is50Lychrel=1
end function
function isPalindrome(num)
if str$(num)=str$(reverse(num)) then isPalindrome=1 else isPalindrome=0
end function
Output Palindrome Lychrel(-like in 50 rounds) numbers:
4994 8778 9999 11811 19591 22822 23532 23632 23932 24542
24742 24842 24942 26362 27372 29792 29892 33933 34543 34743
34943 39493 44744 46064 46164 46364 46564 46964 47274 47574
48284 48584 48684 48884 49394 49794 53935 61916 62826 64046
64346 64446 64846 66466 67976 68486 68986 73537 78587 79297
82928 83038 83238 83638 83838 84448 87378 87578 88388 89198
92229 94449 94549 95359 95759 97479 98089 98289 98389 98589
98989 99999 118811 119911 193391 219912 235532 236632 239932 246642
247742 253352 259952 263362 269962 278872 289982 293392 294492 306603
317713 339933 347743 348843 351153 361163 362263 364463 373373 376673
Palindrome Lychrel count is 100
-
Python:
k, x = 100, 1
while k:
y, z = x, 50
y = y + int(str(y)[::-1])
while str(y) != str(y)[::-1] and z > 0:
y = y + int(str(y)[::-1])
z -= 1
if z == 0 and str(x) == str(x)[::-1]:
print(x)
k -= 1
x += 1
string[::-1] returns reversed string.
-
Hi Tomaaz,
Nice! Do we end on same number?
-
Yes, 376673.
Here is a Ruby version:
k, x = 100, 1
while k > 0
y, z = x, 50
y = y + y.to_s.reverse.to_i
while y.to_s != y.to_s.reverse and z > 0
y = y + y.to_s.reverse.to_i
z -= 1
end
if z == 0 and x.to_s == x.to_s.reverse
puts x
k -= 1
end
x += 1
end
IMHO, it's more clear and easier to read than Python and BASIC. It's a shame that Ruby doesn't really exist outside Rails. :(
-
And if you change the number of sums from 50 to 500, the result is almost exactly the same. Only one number is gone and one new number added.
-
Hi Tomaaz,
HA! I started converting and modifying in SmallBASIC because it would run faster than JB but I started getting very strange results. A real head scratcher until I started printing intermediate results to debug code and realized the error of my ways. SmallBASIC has a precision of 14 decimal places before it gets real with exponents.
So Ruby, like Python can do extended integer math like JB (and probably faster) and SmallBASIC sure can't.
How long did Ruby take to the 500 rounds to see if the Palindrome number was Lychrel like?
Running my revised JB code now.
EDIT: 8 mins later got same set of numbers, dang it I forgot to change j test!
-
Confirmed one new number. (Why aren't the code tags working???) :???
'palindrome Lychrel numbers for JB 2016-01-15 revise better copy than one posted B+=MGA
print "Palindrome Lychrel(-like in 500 rounds) numbers:"
global pcount
while pcount<100
i=i+1
if i=reverse(i) then
t=i+reverse(i)
for j=1 to 500
scan
p=reverse(t)
if p=t then exit for else t=t+p
next j
if j>500 then call show i
end if
wend
print:print "Palindrome Lychrel count is ";pcount
sub show pal
print using("#######",pal);
pcount=pcount+1
if pcount mod 10 =0 then print
end sub
function reverse(num)
snum$=str$(num)
for I=1 to len(snum$)
p$=mid$(snum$,I,1)+p$
next
reverse=val(p$)
end function
Output Palindrome Lychrel(-like in 500 rounds) numbers:
4994 8778 9999 11811 19591 22822 23532 23632 23932 24542
24742 24842 24942 26362 27372 29792 29892 33933 34543 34743
34943 39493 44744 46064 46164 46364 46564 46964 47274 47574
48284 48584 48684 48884 49394 49794 53935 61916 62826 64046
64346 64446 64846 66466 67976 68486 68986 73537 78587 79297
82928 83038 83638 83838 84448 87378 87578 88388 89198 92229
94449 94549 95359 95759 97479 98089 98289 98389 98589 98989
99999 118811 119911 193391 219912 235532 236632 239932 246642 247742
253352 259952 263362 269962 278872 289982 293392 294492 306603 317713
339933 347743 348843 351153 361163 362263 364463 373373 376673 378873
Palindrome Lychrel count is 100
Can you figure the missing number?
-
How long did Ruby take to the 500 rounds to see if the Palindrome number was Lychrel like?
The whole program (calculating and printing all 100 numbers) takes:
50 rounds
Python - 7s.
Ruby - 8s.
500 rounds
Python - 1min. 30s.
Ruby - 6min.
-
Yep! way better times JB takes a couple of minutes for 50 rounds and maybe 8 minutes for 500 rounds while I surf Internet.
Here is code to find the number eliminated going from 50 rounds to 500, kind of ridiculous but I needed practice. I thought it would be a snap but strings from TLOAD were loaded with junk besides the numbers and spaces.
'find missing number.bas for SmallBASIC 0.12.2 2016-01-15 [B+=MGA]
dim a1(120),a2(120)
tload "pl1.txt",pl1,1
tload "pl2.txt",pl2,1
build="":idx1=1:idx2=1
for i=1 to len(pl1)
if instr("1234567890",mid(pl1,i,1)) then
build=build+mid(pl1,i,1)
else
if len(build) then
insert a1,idx1,build
idx1++
build=""
end if
end if
next
build=""
for i=1 to len(pl2)
if instr("1234567890",mid(pl2,i,1)) then
build=build+mid(pl2,i,1)
else
if len(build) then
insert a2,idx2,build
idx2++
build=""
end if
end if
next
for i=1 to (idx1-1)
if a1(i)<>a2(i) then
print a1(i);" is the first number eliminated by increasing rounds from 50 to 500."
exit for
end if
next
pause
Here is code I used to see how many rounds (after 50 and before 500) that one number took to become palindrome.
'Lychrel test for JB 2016-01-15 [B+=MGA]
while 1
print:input "Enter a number to test if Lychrel like in 500 rounds <10 quits ";test
if test<10 then end
t=test+reverse(test)
for j=1 to 500
p=reverse(t)
if p=t then exit for else t=t+p
next j
if j>500 then print test;" is Lychrel-like." else print "Palindrome found for ";test;" in ";j;" rounds."
wend
function reverse(num)
snum$=str$(num)
for I=1 to len(snum$)
p$=mid$(snum$,I,1)+p$
next
reverse=val(p$)
end function
Ha, 52 rounds to become palindrome for the one number eliminated by increasing from 50 to 500.
-
Yep! way better times JB takes a couple of minutes for 50 rounds and maybe 8 minutes for 500 rounds while I surf Internet.
I wasn't really thinking about speed when I created these scripts. So, here is a new version in Ruby:
k, x = 100, 1
while k > 0
while x != x.to_s.reverse.to_i
x += 1
end
y, z = x, 50
y = y + y.to_s.reverse.to_i
while y.to_s != y.to_s.reverse and z > 0
y = y + y.to_s.reverse.to_i
z -= 1
end
if z == 0
puts x
k -= 1
end
x += 1
end
This one takes 0.3 sec for 50 rounds, 2 sec for 500 and 5 min for 5000 rounds.
-
Hi Tomaaz,
Very impressive change, looks about same to me ?? Only one or two guys I know who might beat that speed with their Basics. But I am not seeing any neatly formatted printouts... ;)
OK, so what is .to_s and .to_i, and Ruby has built in "reverse" used about the same way I named my function (great minds think alike) ?
Is Ruby recognized by browsers like JavaScript?
-
Very impressive change, looks about same to me ??
First example tested all numbers, but printed only palindromes. This one's testing only palindromes.
Only one or two guys I know who might beat that speed with their Basics.
I'd love to see a code in BASIC that is as fast and as compact like Ruby or Python example. ;)
OK, so what is .to_s and .to_i, and Ruby has built in "reverse" used about the same way I named my function (great minds think alike) ?
Ruby is fully object oriented language. Everything in Ruby is an object and objects are manipulated by methods (functions in other languages). To apply a method to an object you write a name of the object, a dot and the name of the method. to_s, to_i and reverse are examples of Ruby's method. to_s converts to string. to_i converts to integer. Object oriented code should be read from left to right, like a book. So, x.to_s.reverse.to_i means take x, convert it to string, reverse that string and convert the result to integer. In BASIC it would look like int(reverse(str(x))). IMHO, the Ruby way is easier to follow. Not only it reads from left to right, but you also don't need to follow parenthesis. Python is somewhere in between. It's got old school, BASIC-like int(), str() and len() etc., but majority functions are used the same way Ruby does it - with dot.
Is Ruby recognized by browsers like JavaScript?
No. Ruby is a general purpose language, but it's mostly used as a server side scripting tool (Rails is the most popular framework). However, Ruby can be used on your computer the same way Python or BASIC can. On Linux you can install it from your distro repository (sometimes, it's preinstalled, but not often). On Windows you can get it from http://rubyinstaller.org/ . Alternatively you can try Shoes (http://shoesrb.com/) which is a simple Ruby GUI toolkit.
-
Hi Tomaaz,
Thanks for replies and links (good to get back some BP.org info)! That is an excellent point about parenthesis I had never considered before.
-
I tried this, but with the first number (4994) adding itself to it's reversed version ended up with a number so huge it turned to scientific notation and reversing one of those and then trying to find its value is not going to work :(
D.
-
Hi D,
First off just to be clear, 4994 is Lychrel and so never (theoretically) hits palindrome status again.
2nd you just found Just Basic advantage, extended integer math. This is why I couldn't do this with SmallBASIC as mentioned earlier in this thread.
Were you testing for 50 rounds or 500 rounds?
Here is SmallBASIC code that bombs out with 4994:
'palindrome Lychrel numbers.bas for SmallBASIC 0.12.2 [B+=MGA]
'modified from JB 2016-01-13 B+=MGA and Tomaaz Ruby version
' OH OH OH this won't work in SmallBASIC!!!!!!
' The number precision is lost after 14 digits or less even
'goto test100
'test Lychrel tester
label start
?:input "Enter number to test if Lycrel (<=10 quits) ";test
if test<10 then end
if is50Lychrel(test) then ? "Lychrel" else ? "Not Lychrel"
goto start
'
label test100
i=10:s=" Palindrome Lychrel Numbers"
repeat
i++
if i=reverse(i) then
if is50Lychrel(i) then
print using "#######";i;
pcount++
if pcount%10=0 then print
end if
end if
until pcount=100
?:? space((67-len(s))/2);pcount;s
pause
func reverse(num)
local snum,i,p
snum=str(num)
p=""
for i=1 to len(snum)
p=mid(snum,i,1)+p
next
reverse=val(p)
end
func is50Lychrel(num)
local t,j,p
? "enter is50Lychrel with ";num
is50Lychrel=0
t=num+reverse(num)
print "first test t is ";t
for j=1 to 50
p=reverse(t)
print "p is ";p
if p=t then ? "p=t so exit not Lychrel":exit func else ? "t<>p so continue":t=t+p:? "t is ";t
next j
?"Exit function is Lychrel"
is50Lychrel=1
end
14 places is maximum SmallBASIC can handle before have to get REAL= floats or scientific notation.
As you can see, I was only testing for 50 rounds.
-
Yeah, all numbers in SpecBAS are stored as extended precision floats so scientific notation cuts in at 15 digits and runs through to 19 digits in scientific precision.
Ah well. At least it works (after a fashion!):
10 DEF FN r$(a$)=IIF$(LEN a$<=1,a$,FN r$(a$(2 TO))+a$(1))
20 x=1,k=100: DO WHILE k>0: DO WHILE x<>VAL FN r$(STR$ x): x+=1: LOOP: y=x,z=50,y+=VAL FN r$(STR$ y): DO WHILE (STR$ y<>FN r$(STR$ y)) AND z>0: y+=VAL FN r$(STR$ y),z-=1: LOOP: IF z=0 THEN PRINT x;" ";: k-=1: END IF: x+=1: LOOP
It's basically Tomaaz' code converted to SpecBAS with a simple string reverser function defined.
D.
-
Hi D,
Do I detect a recursive call in your reverse function definition?
-
Hi D,
Do I detect a recursive call in your reverse function definition?
Heh, yes you do - it's a simple way to reverse a string without needing more than one line :)
D.
-
Hi D,
Since you are still here a$(2 to) is some kind of looping through the characters?
Edit: or a$(2 to)+a$(1) is all one structure, I am not seeing how the string is being rebuilt backwards.
-
Hi D,
Since you are still here a$(2 to) is some kind of looping through the characters?
Edit: or a$(2 to)+a$(1) is all one structure, I am not seeing how the string is being rebuilt backwards.
I don't do that - check the brackets :)
D.
-
Hi D,
Since you are still here a$(2 to) is some kind of looping through the characters?
Edit: or a$(2 to)+a$(1) is all one structure, I am not seeing how the string is being rebuilt backwards.
I don't do that - check the brackets :)
D.
Oh, yeah I missed one ) in between, nothing like fresh morning eyes, so SpecBAS has Python like string sections also...
wait this is different '2 to' is ? Time to consult manual... this is very interesting function definition!
-
Hi D,
Since you are still here a$(2 to) is some kind of looping through the characters?
Edit: or a$(2 to)+a$(1) is all one structure, I am not seeing how the string is being rebuilt backwards.
I don't do that - check the brackets :)
D.
Oh, yeah I missed one ) in between, nothing like fresh morning eyes, so SpecBAS has Python like string sections also...
wait this is different '2 to' is ? Time to consult manual... this is very interesting function definition!
String slicing in Sinclair BASIC was revolutionary at the time - while other BASICs were using LEFT$/MID$/RIGHT$, we got the utterly beautiful "TO" argument.
When following any string element (whether it's a literal or variable or the result of a function), you can append a slicer.
a$(1) references the first character in the string. a$(2) the second and so on.
a$(1 TO 10) returns the first ten characters.
a$( TO 10) does the same.
a$(3 TO ) returns the characters in a$ from the third character on.
It's very, very nice.
D.
-
Yes nice to slice, but how you use it in recursive function is amazing.
I mean how it picks one character off adds it to front of a$... until a$ is reversed, I might be misunderstanding or likely NOT understanding how it works.
-
a$(1) references the first character in the string. a$(2) the second and so on.
So what was the syntax for string arrays then?
Dim some$()
Let some$(1)="String 1"
Let some$(2)="String 2"
-
a$(1) references the first character in the string. a$(2) the second and so on.
So what was the syntax for string arrays then?
Dim some$()
Let some$(1)="String 1"
Let some$(2)="String 2"
Back in the day, all strings were arrays of characters. DIM a$(10) returned a ten-character string in a$. SpecBAS is a little different (though still retains that behaviour) in that DIM is more flexible - originally, DIM a$(10,10) returned a 10x10 grid of single characters, which with hindsight was just wrong.
In SpecBAS, arrays take priority - if you're daft enough to declare a$ as a string and also as an array, you're fine until you try to slice it -
A$ will return the variable A$
A$(1) will return the first string in the array a$()
A$(1 TO 5) will return the first 5 characters of the variable a$
A$(1, TO 3) will return the first 5 characters of the first string in the array a$()
It's best to avoid it if possible by not using the same names for variables and arrays!
D.
-
...
Back in the day, all strings were arrays of characters. DIM a$(10) returned a ten-character string in a$. SpecBAS is a little different (though still retains that behaviour) in that DIM is more flexible - originally, DIM a$(10,10) returned a 10x10 grid of single characters, which with hindsight was just wrong.
In SpecBAS, arrays take priority - if you're daft enough to declare a$ as a string and also as an array, you're fine until you try to slice it -
A$ will return the variable A$
A$(1) will return the first string in the array a$()
A$(1 TO 5) will return the first 5 characters of the variable a$
A$(1, TO 3) will return the first 5 characters of the first string in the array a$()
It's best to avoid it if possible by not using the same names for variables and arrays!
D.
That has got to be typo in last example A$(1, to 3) will return first 3 characters of the first string in the array a$ (or A$) not 5. Interesting it is not A$(1)(3).
BTW, if a$ is string and A$ is array is SpecBAS case sensitive? (I am practicing with SpecBAS right now.)
BTW(2) :) no SPACE$(n) nor SPC$(n) for n spaces
-
...
Back in the day, all strings were arrays of characters. DIM a$(10) returned a ten-character string in a$. SpecBAS is a little different (though still retains that behaviour) in that DIM is more flexible - originally, DIM a$(10,10) returned a 10x10 grid of single characters, which with hindsight was just wrong.
In SpecBAS, arrays take priority - if you're daft enough to declare a$ as a string and also as an array, you're fine until you try to slice it -
A$ will return the variable A$
A$(1) will return the first string in the array a$()
A$(1 TO 5) will return the first 5 characters of the variable a$
A$(1, TO 3) will return the first 5 characters of the first string in the array a$()
It's best to avoid it if possible by not using the same names for variables and arrays!
D.
That has got to be typo in last example A$(1, to 3) will return first 3 characters of the first string in the array a$ (or A$) not 5. Interesting it is not A$(1)(3).
Yeah, should be first three chars! Nicely spotted.
BTW, if a$ is string and A$ is array is SpecBAS case sensitive? (I am practicing with SpecBAS right now.)
BTW(2) :) no SPACE$(n) nor SPC$(n) for n spaces
10 LET a big orange bus=20
Is the same as
20 LET ABIGORANGEBUS=20
And who needs SPACE$?
10 LET a$=" "*20
D.
-
print "Thanks, "*100+"D"
EDIT: hmm... even with line number added that might NOT work in SpecBAS.
-
Well finally manage a one liner recursive DEF for reverse function. I know in grand scheme of things this is trivial but I have found it interesting learning.
'recursive reverse.bas for SmallBASIC 0.12.2 [B+=MGA] 2016-01-16 started post 2016-01-20
'test definition of interesting idea for recursive function definition
'deconstruct D's single line reverse function recursive definition, works!
'func revs(s) 'works
' if len(s)=1 then
' revs=s
' else
' revs=revs(mid(s,2))+left(s,1)
' end if
'end
'pack above into two line recursive definition, this needs no additional variable help
'(as doing it in straight forward looping and rebuilding with MID would need
' (at least one var for loop and recommend one for build var) and works!)
'func revs(s):if len(s)=1 then revs=s else revs=revs(mid(s,2))+left(s,1)
'end
' OK try packing down to one-liner
' def wont work the error: "PARAM COUNT ERROR@11 7=F 15,
' ha! So SmallBASIC finds D's one recursive DEF unbelievable too!
'def revs(s)=iff(len(s)=1,s,revs(mid(s,2))+left(s,1) )
' Holy Macaroni!!!
'I was just about to give up on getting SmallBASIC version of D's one liner,
'but decide to try one more thing after trying to decode error message again
'success!!!! from one additional set of parenthesis
def revs(s)=iff( len(s)=1,s,( revs( mid(s,2) ) +left(s,1) ) )
while 1
?:input "Enter a string or number to reverse, just enter quits ";test
if len(test) then ? "reverse of '";test;"' is '";revs(test);"'" else exit
wend
-
Love it :)
Nice job. Btw, that recursive function is a pretty simple one. You should see the version that doesn't use IIF!
10 DEF FN a$(s$)=VAL$ """""+s$(LEN s$)+VAL$ """"""""""+FN a$(s$( TO LEN s$-1))""( TO 2+16*(LEN s$>1))"( TO 2+47*(LEN s$>0))
You see what we had to put up with before SpecBAS came along...
D.
-
Yikes! Spaghetti in one line. :o