Palindromic gapful numbers: Difference between revisions

Content deleted Content added
Nig (talk | contribs)
Undo revision 330973 by Nig (talk)
Nig (talk | contribs)
→‎{{header|AppleScript}}: →‎Translation of Phix "Ludicrously fast …": 6.5-fold speed increase by dumping NSMutableDictionary altogether. Several minor tweaks and comment changes.
Line 178: Line 178:


===Translation of Phix "Ludicrously fast …"===
===Translation of Phix "Ludicrously fast …"===
See [https://www.rosettacode.org/wiki/Palindromic_gapful_numbers#Ludicrously_fast_to_10.2C000.2C000.2C000.2C000.2C000.2C000th the original's] comments for an explanation of the logic. While this AppleScript version is developed directly from the original, comparing the two may be difficult as I've pulled things around a bit for AppleScript efficiency and have relabelled some of the variables for consistency and to try to understand the workings myself! For the set task, it's not actually as fast as the script above, which doesn't have the overhead of being able to handle ridiculously large numbers and which gets the three sets of results for each digit in a single sweep rather than starting at the beginning for each set. It's also not nearly as fast as the Phix original apparently is, taking about twelve seconds to complete the same tests. But that's still not bad. Like the original, it's constrained not by the resolution of the results it can return, but by how many of them it can count using native number classes.
See [https://www.rosettacode.org/wiki/Palindromic_gapful_numbers#Ludicrously_fast_to_10.2C000.2C000.2C000.2C000.2C000.2C000th the original's] comments for an explanation of the logic. While this translation was developed directly from the Phix, comparing the two may be difficult as I've pulled things around a bit for AppleScript efficiency and have relabelled some of the variables for consistency and to help me understand the process myself. On my machine, this version takes 0.12 seconds to carry out the set tasks only a tad slower than the hard-wired script above. The batch of extreme tests in the Phix demo takes around 1.6 seconds.


<lang applescript>use AppleScript version "2.4" -- Mac OS 10.10 (Yosemite) or later.
<lang applescript>-- Return a script object containing the main handlers.

-- Return a script object containing the main handlers.
-- It could be loaded as a library instead if there were any point in having such a library.
-- It could be loaded as a library instead if there were any point in having such a library.
on getWorks()
on getWorks()
script theWorks
script theWorks
property outerX11 : missing value
use framework "Foundation" -- For its NSMutableDictionary class and methods.
property countLimit : missing value
property outerDigit : missing value
property outerDigit_x11 : missing value
property searchLimit : missing value
property to_skip : missing value
property to_skip : missing value
property palcount : missing value
property palcount : missing value
property skipd : {{}, {}, {}, {}, {}, {}, {}, {}, {}}
property skipd : {{}, {}, {}, {}, {}, {}, {}, {}, {}}
property skipdSub : missing value
property skipdOuter : missing value
property output : missing value
property pals : missing value
-- Work out what the remainder would be if the decimal number described as one or two
-- Work out the remainder from the division of the positive decimal integer value which is
-- instances of the digit 'd' with 'gap' zeros between them and 'shift' zeros following,
-- one or two instances of digit 'digit' separated by 'gap' zeros and followed by 'shift' zeros
-- and which may be too large for AppleScript, were divided by the value 'outerDigit_x11'.
-- (which may not be realisable as an AppleScript number) by 'outerX11', a multiple of 11.
on rmdr(d, gap, shift)
on rmdr(digit, gap, shift)
-- Unlike the original Phix, this handler doesn't maintain a large cache, but computes its
-- Remainders from the division of left-shifted decimals by multiples of 11 reliably repeat
-- usable low dividend on the fly by reducing shifts > 9 to equivalents between 4 and 9.
-- every six places shifted > 2, so use a dividend with the equivalent digit shifts < 9.
set multiplier to 10 ^ ((shift - 4) mod 6 + 4)
set coefficient to 10 ^ ((shift - 3) mod 6 + 3)
if (gap > -1) then set multiplier to multiplier + 10 ^ ((gap + shift - 3) mod 6 + 4)
if (gap > -1) then set coefficient to coefficient + 10 ^ ((gap + shift - 2) mod 6 + 3)
return (d * multiplier mod outerDigit_x11) as integer
return (digit * coefficient mod outerX11) as integer
end rmdr
end rmdr
-- Recursively find successive palindromic gapful numbers beginning and ending with
-- Recursively infer from remainder arithmetic any palindromic gapful numbers with
-- 'outerDigit' and keep text versions of those in the required count range.
-- ((count lhs) * 2 + gap) digits whose outer digit is the first value in lhs.
-- Append text versions of any falling in the current keep range to the 'pals' list.
on palindromicGapfuls(lhs, gap, remainder)
on palindromicGapfuls(lhs, gap, remainder)
-- lhs: eg {9, 4, 5} of a potential 9459549 result. (Text eg. "945" in the original.)
-- lhs: eg {9, 4, 5} of a potential 945…549 result.
-- gap: length of inner to be filled in
-- gap: length of inner to be filled in
-- remainder: remainder previously derived from rmdr() results.
-- remainder: remainder of outer, eg 9400049 mod 11, but derived from rmdr() results.
set shift to (count lhs) -- left shift (should always equal length(lhs), I think)
set shift to (count lhs) -- left shift of inner (same as its right shift).
-- Here, 'skipd' isn't a monolithic dictionary with four-element keys, but a list with 9 sublists,
-- This translation's 'skipd' is a four-deep AppleScript list structure indexed with the elements
-- of the original dictionary's keys: (skipd) -> outermost digit -> shift -> gap -> remainder (+ 1).
-- each containing 99 smaller dictionaries with two-element keys. The values of the original
-- The outermost digit element doesn't change during a search based on it, so the script property
-- 'outerDigit' and 'remainder' key elements are used to index this structure. Since 'outerDigit'
-- has but one value per outer digit, the 'outerDigit'th sublist is prefetched and assigned
-- 'skipdOuter' has been preset to skipd's outer-th sublist in the set-up for the current search.
-- to the property 'skipdSub' in the set-up for each sweep.
-- Populate it just enough here to ensure that the slot about to be checked for a 'skip' value exists.
set |key| to {gap, shift}
repeat (shift - (count my skipdOuter)) times
set skip to (item (remainder + 1) of my skipdSub)'s objectForKey:(|key|)
set end of my skipdOuter to {}
end repeat
if (skip is missing value) then -- Key not in dictionary. Set 'skip' high for the 'if' test below.
set skip to searchLimit
repeat (gap - (count item shift of my skipdOuter)) times
set end of item shift of my skipdOuter to {}
else
set skip to (skip as real) div 1
end repeat
repeat (remainder + 1 - (count item gap of item shift of my skipdOuter)) times
end if
if (palcount + skip > to_skip) then -- (Also if they're equal, originally.)
set end of item gap of item shift of my skipdOuter to missing value
end repeat
set skip to item (remainder + 1) of item gap of item shift of my skipdOuter
if ((skip is missing value) or (palcount + skip > to_skip)) then
set skip to 0
set skip to 0
set template to lhs & missing value & reverse of lhs -- Palindrome template list.
set mid to shift + 1 -- Index of its middle position.
set nextGap to gap - 2
set nextGap to gap - 2
repeat with d from 0 to 9
repeat with d from 0 to 9
set nextRem to (remainder + rmdr(d, nextGap, shift)) mod outerDigit_x11
set nextRem to (remainder + rmdr(d, nextGap, shift)) mod outerX11
if (gap > 2) then
if (gap > 2) then
set skip to skip + palindromicGapfuls(lhs & d, nextGap, nextRem)
set skip to skip + palindromicGapfuls(lhs & d, nextGap, nextRem)
else if (nextRem is 0) then
else if (nextRem is 0) then
-- A palindrome of lhs's contents around gap ds would be … gapful.
set palcount to palcount + 1
set palcount to palcount + 1
if (palcount > to_skip) then
if (palcount > to_skip) then
if (gap is 2) then
-- This one would be in the current keep range, so realise it as text and store it.
set item mid of template to {d, d} -- Not d*11 in case d is 0.
if (gap is 2) then set d to {d, d} -- Not d * 11 as d could be 0.
else -- if (gap is 1) then
set end of my pals to (lhs & d & reverse of lhs) as text
set item mid of template to d
-- else if (gap is 0) then
-- set item mid of template to ""
-- else
-- set template to d
end if
set end of my output to template as text
else
else
set skip to skip + 1
set skip to skip + 1
end if
end if
end if
end if
if (palcount = searchLimit) then exit repeat
if (palcount = countLimit) then exit repeat
end repeat
end repeat
if (palcount < to_skip) then (item (remainder + 1) of my skipdSub)'s setObject:(skip) forKey:(|key|)
if (palcount < to_skip) then set item (remainder + 1) of item gap of item shift of my skipdOuter to skip
else
else
set palcount to palcount + skip
set palcount to palcount + skip
Line 264: Line 255:
end palindromicGapfuls
end palindromicGapfuls
-- Return a list of numeric texts representing the last 'keep' of the first 'searchLimit'
-- Set up a search for the last 'keep' of the first 'countLimit' PGNs > 100 whose outer digit is 'outer',
-- call the recursive process for each palindrome width, and eventually return the stored numeric texts.
-- palindromic gapful numbers > 100 which begin and end with 'digit'.
on collect(digit, searchLimit, keep)
on collect(outer, countLimit, keep)
-- Initialise these script object properties for the current search.
-- Initialise script object properties for the current search.
set outerDigit to digit
set outerX11 to outer * 11
set outerDigit_x11 to outerDigit * 11
set my countLimit to countLimit
set my searchLimit to searchLimit
set to_skip to countLimit - keep
set to_skip to searchLimit - keep
set palcount to 0
set palcount to 0
set skipdSub to item outerDigit of my skipd
set skipdOuter to item outer of my skipd
set output to {}
set pals to {}
-- Locals.
-- Also locals and TIDs.
set gap to 1 -- Initially 1 digit between the outer pair.
set lhs to {outer}
set lhs to {outerDigit} -- Initial left end of palindomes.
set gap to 1 -- Number of digits between outer pair.
set astid to AppleScript's text item delimiters
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
set AppleScript's text item delimiters to "" -- For list-to-text coercions.
repeat until (palcount = searchLimit)
repeat until (palcount = countLimit)
set remainder to rmdr(outerDigit, gap, 0)
set remainder to rmdr(outer, gap, 0)
palindromicGapfuls(lhs, gap, remainder)
palindromicGapfuls(lhs, gap, remainder)
set gap to gap + 1
set gap to gap + 1
Line 287: Line 277:
set AppleScript's text item delimiters to astid
set AppleScript's text item delimiters to astid
return output
return pals
end collect
end collect
on initialiseSkipd()
repeat with thisList in my skipd
repeat 99 times
set end of thisList to current application's class "NSMutableDictionary"'s new()
end repeat
end repeat
end initialiseSkipd
end script
end script
tell theWorks to initialiseSkipd()
return theWorks
return theWorks
end getWorks
end getWorks

(* Test code *)

-- Return an integer as text with the appropriate English ordinal suffix
on ordinalise(n)
-- Adapted from Victor Yee (adapted from NG (adapted from Jason Bourque) & Paul Skinner)
set units to n mod 10
if ((units > 3) or ((n - units) mod 100 is 10) or (units < 1) or (units mod 1 > 0)) then return (n as text) & "th"
return (n as text) & item units of {"st", "nd", "rd"}
end ordinalise


on doTask()
on doTask()
Line 310: Line 301:
{1.0E+13, 1, 9, 9}, {1.0E+14, 1, 9, 9}, ¬
{1.0E+13, 1, 9, 9}, {1.0E+14, 1, 9, 9}, ¬
{1.0E+15, 1, 2, 4}}
{1.0E+15, 1, 2, 4}}
-- set tests to {{20, 20, 1, 9}, {100, 15, 1, 9}, {1000, 10, 1, 9}} -- The set task.
-- set tests to {{20, 20, 1, 9}, {100, 15, 1, 9}, {1000, 10, 1, 9}} -- The RC task.
set output to {}
set output to {}
Line 317: Line 308:
set AppleScript's text item delimiters to " "
set AppleScript's text item delimiters to " "
repeat with i from 1 to (count tests)
repeat with i from 1 to (count tests)
set {searchLimit, keep, firstEndDigit, lastEndDigit} to item i of tests
set {countLimit, keep, firstOuter, lastOuter} to item i of tests
set |from| to searchLimit - keep + 1
if (countLimit = keep) then
set h to "First " & countLimit
if (searchLimit = keep) then
set r to "First " & searchLimit
else if (keep > 1) then
else if (keep > 1) then
set r to "Last " & keep & " of first " & searchLimit
set h to "Last " & keep & (" of first " & countLimit)
else
set h to ordinalise(countLimit)
end if
if ((keep = 1) and (firstOuter = lastOuter)) then
set h to h & " palindromic gapful number > 100 ending with " & firstOuter & ":"
else if (firstOuter = lastOuter) then
set h to h & " palindromic gapful numbers > 100 ending with " & firstOuter & ":"
else
else
set r to (searchLimit as text) & "th"
set h to h & " palindromic gapful numbers > 100 ending with digits from " & firstOuter & (" to " & lastOuter & ":")
end if
end if
set s to " > 100 ending with "
if (i > 1) then set end of output to ""
if (keep > 1) then set s to "s" & s
set end of output to h
set t to (lastEndDigit as text) & ":"
repeat with outer from firstOuter to lastOuter
set end of output to theWorks's (collect(outer, countLimit, keep)) as text
if (firstEndDigit is not lastEndDigit) then set t to "digits " & firstEndDigit & " to " & t
set end of output to ""
set end of output to r & " palindromic gapful number" & s & t
repeat with digit from firstEndDigit to lastEndDigit
set end of output to theWorks's (collect(digit, searchLimit, keep)) as text
end repeat
end repeat
end repeat
end repeat
set AppleScript's text item delimiters to linefeed
set AppleScript's text item delimiters to linefeed
set output to (rest of output) as text
set output to output as text
set AppleScript's text item delimiters to astid
set AppleScript's text item delimiters to astid
Line 346: Line 339:


{{output}}
{{output}}
<pre style="font-size:80%">First 20 palindromic gapful numbers > 100 ending with digits 1 to 9:
<pre style="font-size:80%">First 20 palindromic gapful numbers > 100 ending with digits from 1 to 9:
121 1001 1111 1221 1331 1441 1551 1661 1771 1881 1991 10901 11011 12221 13431 14641 15851 17171 18381 19591
121 1001 1111 1221 1331 1441 1551 1661 1771 1881 1991 10901 11011 12221 13431 14641 15851 17171 18381 19591
242 2002 2112 2222 2332 2442 2552 2662 2772 2882 2992 20702 21912 22022 23232 24442 25652 26862 28182 29392
242 2002 2112 2222 2332 2442 2552 2662 2772 2882 2992 20702 21912 22022 23232 24442 25652 26862 28182 29392
Line 357: Line 350:
9009 9999 94149 99099 900009 909909 918819 927729 936639 945549 954459 963369 972279 981189 990099 999999 9459549 9508059 9557559 9606069
9009 9999 94149 99099 900009 909909 918819 927729 936639 945549 954459 963369 972279 981189 990099 999999 9459549 9508059 9557559 9606069


Last 15 of first 100 palindromic gapful numbers > 100 ending with digits 1 to 9:
Last 15 of first 100 palindromic gapful numbers > 100 ending with digits from 1 to 9:
165561 166661 167761 168861 169961 170071 171171 172271 173371 174471 175571 176671 177771 178871 179971
165561 166661 167761 168861 169961 170071 171171 172271 173371 174471 175571 176671 177771 178871 179971
265562 266662 267762 268862 269962 270072 271172 272272 273372 274472 275572 276672 277772 278872 279972
265562 266662 267762 268862 269962 270072 271172 272272 273372 274472 275572 276672 277772 278872 279972
Line 368: Line 361:
95311359 95400459 95499459 95588559 95677659 95766759 95855859 95944959 96033069 96122169 96211269 96300369 96399369 96488469 96577569
95311359 95400459 95499459 95588559 95677659 95766759 95855859 95944959 96033069 96122169 96211269 96300369 96399369 96488469 96577569


Last 10 of first 1000 palindromic gapful numbers > 100 ending with digits 1 to 9:
Last 10 of first 1000 palindromic gapful numbers > 100 ending with digits from 1 to 9:
17799771 17800871 17811871 17822871 17833871 17844871 17855871 17866871 17877871 17888871
17799771 17800871 17811871 17822871 17833871 17844871 17855871 17866871 17877871 17888871
27799772 27800872 27811872 27822872 27833872 27844872 27855872 27866872 27877872 27888872
27799772 27800872 27811872 27822872 27833872 27844872 27855872 27866872 27877872 27888872
Line 379: Line 372:
9675005769 9675995769 9676886769 9677777769 9678668769 9679559769 9680440869 9681331869 9682222869 9683113869
9675005769 9675995769 9676886769 9677777769 9678668769 9679559769 9680440869 9681331869 9682222869 9683113869


Last 5 of first 10000 palindromic gapful numbers > 100 ending with digits 1 to 9:
Last 5 of first 10000 palindromic gapful numbers > 100 ending with digits from 1 to 9:
1787557871 1787667871 1787777871 1787887871 1787997871
1787557871 1787667871 1787777871 1787887871 1787997871
2787557872 2787667872 2787777872 2787887872 2787997872
2787557872 2787667872 2787777872 2787887872 2787997872
Line 390: Line 383:
968697796869 968706607869 968715517869 968724427869 968733337869
968697796869 968706607869 968715517869 968724427869 968733337869


100000th palindromic gapful number > 100 ending with digits 1 to 9:
100000th palindromic gapful numbers > 100 ending with digits from 1 to 9:
178788887871
178788887871
278788887872
278788887872
Line 401: Line 394:
96878077087869
96878077087869


1000000th palindromic gapful number > 100 ending with digits 1 to 9:
1000000th palindromic gapful numbers > 100 ending with digits from 1 to 9:
17878799787871
17878799787871
27878799787872
27878799787872
Line 412: Line 405:
9687870990787869
9687870990787869


10000000th palindromic gapful number > 100 ending with digits 1 to 9:
10000000th palindromic gapful numbers > 100 ending with digits from 1 to 9:
1787878888787871
1787878888787871
2787878888787872
2787878888787872
Line 444: Line 437:
96878787878786133168787878787869
96878787878786133168787878787869


1.0E+15th palindromic gapful number > 100 ending with digits 2 to 4:
1.0E+15th palindromic gapful numbers > 100 ending with digits from 2 to 4:
27878787878787888878787878787872
27878787878787888878787878787872
3087878787878783113878787878787803
3087878787878783113878787878787803