(06-15-2017 09:12 PM)pier4r Wrote: Nice, the performance is the one of lrot7?

Short answer: yes.

There's not as much time savings as I had originally hoped with the LRLL/LRLLD commands. Probably means that the bulk of the time is spent in the copying. That's not a big surprise, but when you consider that on a 50g the copying is done by a routine that runs at ARM level (instead of Saturn), I was hoping that the initial split point computation was a bigger part of the overall time spent. Here's how the timings look for the new LROT/LRLL/LRLLD:

So LRLLD is slightly faster than the updated LROT. Not anywhere near as fast as the FOR/GET loop, but you have to remember that it's actually doing more than simply getting 1 list element. It literally moves all 1000 of the list elements every time through the loop. So I don't feel too bad about the performance.

I also tested the new command LHDTL, which performed nicely. Not as fast as DOLIST or DOSUBS, but better than FOR/GET and DUP TAIL SWAP HEAD.

(06-18-2017 02:28 PM)pier4r Wrote: @DavidM: is there any repository where one can download a "pre-release" of your library or one has to wait until you upload it here?

I've attached the latest version. It wasn't until recently that I realized I was inadvertently spamming your challenge thread with these library diversions. The library was a direct result of working on the challenges, but in retrospect it seems off-topic enough to warrant a separate thread. Apologies to everyone!

The release notes and command summaries are included below. Full command descriptions are included in the attached zip.

Teaser:

I've completed several of the pieces needed for DOPERM and DOCOMB commands, and the results so far seem to support continuing that effort. As an example, a test that generates all 5040 permutations of a 7-element list completes in about 125 seconds on a 50g. That's about 40 permutations/second just being dumped on the stack, which seems fast enough to consider using these methods to "feed" combinations/permutations to a user-supplied program. It's likely that any user-supplied program will be the most significant contributor to the overall performance, of course. But at least the underpinnings seem to be working well.

Version 0.8.0d
2017-06-18

- Added new commands:
LHDTL, LRLL, LRLLD, LSEQ, LSEQR

- Changed LSPLT error condition: a sublist size argument that is greater than the actual list size no longer generates an error. In that situation, the full list will be returned as the first sublist.

COMMAND SUMMARY

LCLLT - collates a list of sublists
LCNT - counts objects in a list
LDDUP - removes duplicates from a list
LDST - distributes list items into sublists (reciprocal of LCLLT)
LEQ - checks list items to see if any do not match
LGRP - replaces repeated elements with a single instance
LHDTL - retrieves the first element in a list while leaving the rest on the stack
LMRPT - repeats list contents as indicated by count
LNDUP - creates a list by repeating an object as indicated by count
LREPL - replaces list elements with a substitute object as indicated
LRLL - rolls the list (equivalent to 1 LROT)
LRLLD - "roll down" for the list (equivalent to -1 LROT)
LROT - rotates list elements left or right as indicated by count
LRPCT - list with LGRP elements and another list of the count of each element
LSDIV - subdivides a list into <count> sublists
LSEQ - creates a list of <count> integers
LSEQR - creates a list of integers for the range specified
LSHF - shuffles the contents of a list
LSPLT - splits a list as indicated into two sublists
LSUM - ΣLIST that also handles lists with 0 or 1 element
LSWP - swaps the position of two list elements
LXIL - explodes inner sublists into individual elements (non-recursive)
LXILR - recursive version of LXIL
S→NL - converts a string to a list of numbers
NL→S - converts a list of numbers to a string
S→SL - converts a string to a list of characters
SL→S - converts a list of characters to a string

(06-18-2017 03:23 PM)DavidM Wrote: I've attached the latest version. It wasn't until recently that I realized I was inadvertently spamming your challenge thread with these library diversions. The library was a direct result of working on the challenges, but in retrospect it seems off-topic enough to warrant a separate thread. Apologies to everyone!

As I said already, I don't mind because it is a library for list processing in a thread of list processing (I mean, rarely the off topic is so close to the topic). Anyway even if you want to make a new thread, start it, otherwise every question that I have will end up here because it is the closest related thread.

I learn a new technique every time I see your code, Werner. It will probably take a while for me to decipher everything that it's doing, but that should be expected for an embedded recursive routine inside of list that is EVAL'd. Very creative!

I made that a couple of years ago and it's version 9 ;-)
I have DOPERM routines as well, even 2 flavours, as explained in the Vietnamese puzzle post, one to simply go over all permutations (like DOCOMB), and one that will allow you to skip branches at a certain 'level'.

Going through the algorithm for #34 . The idea is to feed one value at time (without arranging optimally the list in input beforehand, also, shuffle it a bit to be sure it is messy) and perform insertions in a balanced search binary tree (that has to keep the balancing).

As usual in this cases, I go first through the problem on my own. If I struggle too much without progress, I check what was done by the others or I ask in internet communities. Otherwise I appreciate/learn less.

Those cases remember me that before having shiny frameworks that helps you, one first has to understand the procedure by himself.

(06-25-2017 11:50 AM)pier4r Wrote: Going through the algorithm for #34 . The idea is to feed one value at time (without arranging optimally the list in input beforehand, also, shuffle it a bit to be sure it is messy) and perform insertions in a balanced search binary tree (that has to keep the balancing).

Pier, you are an artist as well as a calculator enthusiast! You're also way ahead of me in the challenges as I have fallen behind while, uhm, working on other things.

Here's a couple submissions for #14 and #15, which I opted to do with only the built-in commands. I assumed (perhaps wrongly) that the list wouldn't begin or end with the sentinel (0). They're almost identical, but I suppose that's to be expected:

@ convert the list digits to integers in base 10
list
1
\<<
IF DUP THEN @ if the current digit isn't 0, then...
num base * + @ multiply current num by base and add new digit
ELSE @ else the digit is the separator (0)
num SWAP @ place current num on stack and reset running total to 0
END
'num' STO @ save the updated num
\>>
DOSUBS @ process the list

@ subtract the second number from the first
EVAL num -

@ obtain the sign and ABS of the difference
DUP SIGN SWAP ABS

@ digit counter, for later grouping into final list
0

@ put the difference in SL1
SWAP

@ convert number to digits
WHILE @ do while the running result > 0
DUP
REPEAT
10 IDIV2 @ determine quotient (SL2) and remainder (SL1)
ROT 1 + ROT @ update digit counter and move current digit into place
END

@ convert the digits to integers in base 10
list
1
\<<
IF DUP THEN @ if the current digit isn't 0, then...
num base * + @ multiply current num by base and add new digit
ELSE @ else the digit is the separator (0)
num SWAP @ place current num on stack and reset running total to 0
END
'num' STO @ save the updated num
\>>
DOSUBS @ process the list

@ add the second number to the first
EVAL num +

@ obtain the sign and ABS of the difference
DUP SIGN SWAP ABS

@ digit counter, for later grouping into final list
0

@ put the difference in SL1
SWAP

@ convert number to digits
WHILE @ do while the running result > 0
DUP
REPEAT
base IDIV2 @ determine quotient (SL2) and remainder (SL1)
ROT 1 + ROT @ update digit counter and move current digit into place
END

You may already be aware of this, and I couldn't remember if this has already been mentioned: the first example for #15 appears to have an incorrect result. Shouldn't it be 201 (base 4) instead of 111?

#16 has three parts, and I wasn't about to skip using my newfangled DOPERM command for these:

Code:

C1601
\<<
@ given list defined in problem description
{ 0 8 8 2 }

@ convert to list of characters
48 ADD CHR

@ size of list (needed by DOPERM)
DUP SIZE

@ execute for each permutation
\<<
@ convert character list to string, then convert string to number
SL\->S STR\->

@ determine if it is a multiple of 117
DUP 117 MOD

@ drop the result if it is not a multiple of 117
NOT NOT DROPN
\>>
DOPERM

@ remove duplicates
LDDUP
\>>

C1602
\<<
@ given list defined in problem description
{ 1 3 4 }

@ convert to list of characters
48 ADD CHR

@ size of list (needed by DOPERM)
DUP SIZE

@ execute for each permutation
\<<
@ convert character list to string, then convert string to number
SL\->S STR\->

@ is the current number a prime?
DUP ISPRIME?

@ if not, drop it
NOT DROPN
\>>
DOPERM

@ remove duplicates (not strictly needed for the given argument, but good to have)
LDDUP

\>>

C1603
\<<
@ given list defined in problem description
{ 6 4 0 9 }

@ convert to list of characters
48 ADD CHR

@ size of list (needed by DOPERM)
DUP SIZE

@ execute for each permutation
\<<
@ convert character list to string, then convert string to number
SL\->S STR\->

@ find cube root of the current number
DUP 3 XROOT \->NUM

@ if the cube root is an integer, then...
IF DUP FP 0 == THEN
@ convert it to an exact number and place in a list with the permutation
R\->I 2 \->LIST
ELSE
@ not an integer, drop both the permutation and the cube root
DROP DROP
END
\>>
DOPERM
\>>

#17 is very straightforward with DOPERM. It appears that my 50g is OK with up to 7 digits for this particular exercise. 8 digits gives a result of (8!-1) entries (40319), which well exceeds the memory available for the stack. Here's the submission:

Code:

Ch17
\<<
@ convert to list of characters
48 ADD CHR

@ size of list (needed by DOPERM)
DUP SIZE

@ execute for each permutation
\<<
@ output the permutation as a number
SL\->S STR\->
\>>
DOPERM

@ explode final list and drop the first permutation
LIST\-> ROLL DROP
\>>

No I'm not ahead of you at all (thanks for the compliment but I have to be honest) . It is the forum giving the wrong impression due to my amount of posts. I just collect challenges that I think could be interesting to help learning list processing in rpl, but I didn't solve all of them, I always pick the next one that inspires me.

For example I thought that the #14 and #15 were harder than your relatively compact code so I put them down in the todo list. (and yes I need to check the problem statement, thanks for the feedback)
I may have a look at them after #34.

Furthermore you started to make an interesting library so just to use it (or to see if some commands are missing) I'm trying to coming up with new challenges - once I solve the one I'm on - since I'd like to use your effort instead of letting it be semi hidden in the forum.

Actually it seems to me that using lists to process trees is a good direction, plus I refresh some algorithm and data structures (but little math is involved , my other thread has to wait)

So for the #37 I have the solution below (that heavily uses commands of the library of DavidM to keep speed and debugging ability). Surely it can be polished to be speedy, but first and foremost comes readability/maintainability then maybe speed (I guess I'll have to try newRPL on this one), especially for not so straightforward code.

I call for help because I am not sure about the results reported.

The #37 requires to create, given a list of N teams (N: even), all the valid match days ignoring home/away . So {1 2} and {2 1} are considered the same.
A match day contains all the teams in pairings, and a team appears only once.

Fist idea to solve this: backtracking. I am not really fan of the idea, because it searches through every possible combination and it is a bit clumsy.

Moreover, unless I am mistaken, with 4 teams I have 6 possible pairs ( 4 over 2). With those 6 pairs I can create an upper bound of 15 match days (6 over 2) (the valid ones are less though). With 8 teams I have 28 possible pairs (8 over 2) and an upper bound of 20475 match days ( 28 over 4).
So a backtracking approach would take a bit.

Therefore my plan was/is the following (feel free to produce better approaches!):
- start with a valid match day (the basic one, that pairs consecutive teams in the list)
- then swap two teams (unless they face each other)
- I should have another valid match day
- the problem is that the new valid match day may be equal to an existing one, just with pairs swapped or teams swapped in the same pair. To solve this I need an hash function to compare match days, because if I go with comparing the structure of a match day (a list of lists) it is too costly.
- After a bit of trials it seems that if I consider the sum of the elements in a pair (teams are unique positive integer numbers) and I multiply those sums in a match day, I get an unique number. So if I have (1,2);(3,4) I get 3*7=21 ; if I have (2,1);(3,4) I get the same; if I have (2,3);(1,4) I get 25 and so on.
The problem is that I did not prove it. I have the feeling it works because I was not able to build a counterexample after some trials, but, well, no real proof.
- then when I end all the possible swaps between teams, I consider the current match day "exhausted" as generator for further match days. So I consider the next valid match day - not yet used as generator for new ones - that I found with the method above.
- It ends when no valid match days are left that may generate new ones using the method above.

Due to how I build the match days and my hash function, I am not really sure I get all the valid match days or if I get duplicates (given the constraints).

With 8 teams, for example, I get 67 valid match days and for the sample checks done (I checked like 20 of them), they are all different.

Any suggestion for a better approach? Or any suggestion to prove that the approach is valid?

#37

Code:

\<<
@ Plan:
@ I could go via backtracking but I do not really like the technique.
@ I may employ something different that is even worse but at least
@ it is more appealing to me.
@ So the idea is the following:
@ - generate one match day (one it is easy)
@ - then go through all the possible pairings of the teams and swap the teams.
@ So if the matchday is ( 1 vs 2 ; 3 vs 4 ; 5 vs 6) one may swap 2 and 3
@ getting the new matchday ( 1 vs 3 ; 2 vs 4 ; 5 vs 6) . Of course does
@ not make sense to swap teams if those are already matching each other
@ (home and away not important).
@ - verify that the new match day, that is valid by construction, is not already
@ collected, if it is not, collect it.

@ note 22:26 11.08.2017: since I am lazy, I do not really like the idea of comparing every
@ matchday valid until now, ignoring the order of matches but considering only
@ pairings. So what could I do? I am not sure but a sort of hash function
@ would help. Hash function that should ignore the order of the pairings.
@ So I thought about using the absolute differences between paired teams and then summing them
@ or multiplying them. But it does not work. For example (1,2);(3,4);(5,7);(6,8) and (1,3);(2,4);(5,6);(7,8)
@ then an idea is to multiply the sum of the pairings.
@ I am not sure it works though, I did not prove it. For the moment I do not
@ see counterexamples so I use it.

@create a first simple matchday (pair all the teams one after another)
1 sizeTeamsList
FOR index
@ extract a pair, add it to the list.
teamsList index index 1 + SUB
@ make a sublist
1 \->LIST
'currentMatchDayList' SWAP STO+
2 STEP

@currentMatchDayList
@test: so far it works,

currentMatchDayList
\<< \GSLIST \>>
DOSUBS
@we should have the list of sums of each pairing.
\PILIST
@we have the product.
'tempHashValue' STO

@ we save the current matchday as valid matchday
@ and as base matchday . A base matchday can be used to generate new ones
'listValidMatchDays' currentMatchDayList 1 \->LIST STO+
'baseMatchDays' currentMatchDayList 1 \->LIST STO+

firstIterationList LHDTL 'swapTeam1' STO
DUP 'firstIterationList' STO
'secondIterationList' STO
@we store the rest list
@for the next first iteration and next second iteration.

@we will use this later
newMatchDayList swapTeam1 LPOSL 'basePosSwapTeam1' STO

WHILE
secondIterationList SIZE 0 >
REPEAT
@reset otherwise we lose track of the current match day structure.
currentMatchDayList 'newMatchDayList' STO

secondIterationList LHDTL 'swapTeam2' STO
'secondIterationList' STO

@ now we need to swap the position in the pairings between team1 and team2
@ by construction those teams should be in some pairings
newMatchDayList swapTeam2 LPOSL 'basePosSwapTeam2' STO

IF
basePosSwapTeam1 basePosSwapTeam2 \=/
@if the two teams are not directly paired
THEN
@get the pairings and swap teams
newMatchDayList basePosSwapTeam1 GET
@get the pairings with team1
DUP swapTeam1 POS
@get position of team1
swapTeam2 1 \->LIST
REPL
@replace with team2
1 \->LIST
@for having a sublist for later
'modifiedPairing' STO

@now by construction the match day so generated is valid
@(since we just swapped two teams that were not challenging each other)
@we need to see if we do not have it already.
newMatchDayList
\<< \GSLIST \>>
DOSUBS
@we should have the list of sums of each pairing.
\PILIST
@we have the product.
'tempHashValue' STO

'listValidMatchDaysHash' tempHashValue STO+
'baseHashValues' tempHashValue STO+
END @if add new matchday
END @ If teams are not in the same pairing.
END @while second team
END @while firsTteam
END @while for baseMatchDays

#37 may be more complicated than it first appears. While the 4-team example is fairly straightforward, I need some clarification on the problem for larger team counts.

Let's say you have 6 teams. One possible set of pairings is:

1-2 3-4 5-6

Unlike the case of a 4-team scenario, however, there's the possibility of multiple match-days that still share a common match. Using the above example, here's three potential match-days that all have a 5-6 match:

1-2 3-4 5-6
1-3 2-4 5-6
2-3 1-4 5-6

My question is: should the output include all three of these as match-days? If not, which is the "correct" one, and what makes it so?

So, after having done the #37 in userRPL (as reported above, I am not 100% sure that all the possible match days are found, nor duplicates are excluded), I realized that my algorithm is not that fast (and no I am not fan of "full stack no variables").

So I started to search another platform that is as quick, for development, as userRPL. really RPL is amazing.

I tried newRPL since it has the new integration for text files. Unfortunately it does not run in my winXP machine (machine for the 50g). It has to wait until I setup the win10 system.

I tried RPL/2, since it is a great project that deserved to be used too, but the last version of the 4.0.x release does not compile easily in cygwin 2.5.2 , I spent some hours on it but nothing. I'll try another time.

I completely forgot about the HP prime on android (those are the moment I need to use that beast), damn me, and so the closest small and quick language that I know that could do the task is awk (although it lacks of many array manipulation functions or central repositories for libraries, well I do them myself, even if poorly. Python with no curly braces or blocks defined not only by empty lines is not really liked by me)

Would be helpful if someone else can match the 8 teams matchdays. If there are more or less.

PS: of course this will lead to the next challenge. Given the list of all matchdays, find all the possible valid schedules. That is, with N teams, find N-1 matchdays where every team is paired with any other team. But I will have to clarify it better in the first post.

So likely my program does not found all valid match days, but still I did not find duplicates. I need to redesign my hash function.

Edit2: second quick approach. Still not foolproof, just working on intuition and missing counterexamples.
A pair is composed by two different numbers that won't appear in other pairs (given a match day).
So while the sum may match for two different pairings, it is unlikely (I would say impossible, but I did not formalized this), that the sum and the absolute difference between two numbers will match.

Nevertheless having x+y=s and |x-y|=d should describe the two numbers better than the only sum. Then to "combine" this information I use the multiplication, since a number can be determined by its factors uniquely.

So now I do not have only the products of the sum of the teams in each pair, but the product of the sum and the absolute difference between the teams in each pair.

I've got a work-in-progress version of a routine that attempts to do this on a 50g, but it's still got some major issues. I keep seeing "reflected" match days in the results as the team size grows, and it slows down intolerably at >6 teams.

If I sort and de-dup the results, it appears to come up with reasonable-looking pairings. I haven't validated them beyond checking for duplicates and just visually scanning for "gross and obvious" failures, so I'm not claiming this to be complete or accurate. I had to leave it running for a while on an emulated 50g (I didn't time it but I think it was >1 hour), so the current approach isn't really viable. I'm attaching my 8-team result so that you might be able to compare the match-days with yours. My current test found 105 possible pairings: