Cycles of a permutation
You are encouraged to solve this task according to the task description, using any language you may know.
On the event of their marriage, Alf and Betty are gifted a magnificent and weighty set of twenty-six wrought iron letters, from A to Z, by their good friends Anna and Graham.
Alf and Betty have, in their home, a shelf sturdy enough to display their gift, but it is only large enough to hold fifteen of the letters at one time. They decide to select fifteen of the letters and rearrange them every day, as part of their daily workout, and to select a different set of letters from time to time, when they grow bored of the current set.
To pass the time during their honeymoon, Alf and Betty select their first set of letters and find seven arrangements, one for each day of the week, that they think Anna and Graham would approve of. They are:
Mon: HANDY COILS ERUPT Tue: SPOIL UNDER YACHT Wed: DRAIN STYLE POUCH Thu: DITCH SYRUP ALONE Fri: SOAPY THIRD UNCLE Sat: SHINE PARTY CLOUD Sun: RADIO LUNCH TYPES
They decide to write down instructions for how to rearrange Monday's arrangement of letters into Tuesday's arrangement, Tuesday's to Wednesday's and so on, ending with Sunday's to Monday's.
However, rather than use the letters, they number the positions on the shelf from 1 to 15, and use those position numbers in their instructions. They decide to call these instructions "permutations".
So, for example, to move from the Wednesday arrangement to the Thursday arrangement, i.e.
Position on shelf: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Wed: D R A I N S T Y L E P O U C H Thu: D I T C H S Y R U P A L O N E
they would write the permutation as:
Wed: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Thu: 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10
(i.e. The D that is in position 1 on Thursday stayed in position 1, the I that is in position 2 on Thursday came from position 4, the T in position 3 came from position 7, and so on.)
They decide to call this "two-line notation", because it takes two lines to write it down. They notice that the first line is always going to be the same, so it can be omitted, and simplify it to a "one-line notation" that would look like this:
Wed->Thu: 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10
As a subtle nuance, they figure out that when the letters at the right hand end don't move, they can safely leave them off the notation. For example, Mon and Tue both end with T, so for Mon->Tue the one-line notation would be fourteen numbers long rather than fifteen.
Then they notice that the letter at position 9 moves to position 12, the letter at position 12 moves to position 13, and the letter at position 13 moves to position 9, forming the cycle 9->12->13->9->12->13-> etc., which they decide to write as (9 12 13). They call this a 3-cycle, because if they applied the cycle to the letters in those positions three times, they would end up back in their original positions.
They also notice that the letters in positions 1 and 6 do not move, so they decide to not write down any of the 1-cycles (not just the ones at the end as with the one-line notation.) They also decide that they will always write cycles starting with the smallest number in the cycle, and that when they write down the cycles in a permutation, the will be sorted by the first number in the permutation, smallest first.
The permutation Wed->Thur has a 10-cycle starting at position 2, a 3-cycle starting at position 9 and two 1-cycles (at positions 1 and 6), so they write down:
Wed->Thu: (2 8 7 3 11 10 15 5 14 4) (9 12 13)
They decide to call this "cycle notation". (By pure coincidence all the names they have chosen are the same as those used by mathematicians working in the field of abstract algebra. The abstract algebra term for converting from one-line notation to cycle notation is "decomposition". Alf and Betty probably wouldn't have thought of that. 1-cycles, 2-cycles, 3-cycles et cetera are collectively called k-cycles. 2-cycles are also called transpositions. One more piece of terminology: two or more cycles are disjoint if they have no elements in common. The cycles of a permutation written in cycle notation are disjoint.)
Alf and Betty notice that they can perform the first k-cycle as a series of transpositions, swapping the letters in positions 14 and 4, then the letters in positions 5 and 14, then 15 and 5, then 10 and 15 and so on, ending with swapping the letters in positions 4 and 2.
Then it occurs to them that it would be more efficient if one of them took the letter in position 8 and held it while the other moved the letter in position 7 and moved it to position 8, then moved 3 to 7, 11 to 3 and so on. Finally the one who had been holding the letter from position 8 could put it in position 2.
Computer programmers would call this an "in-place" solution. The one and two-line notations lend themselves to a not-in-place solution, i.e., having a second shelf that they could conveniently move they letters to while rearranging them.
If they accidentally did the Wed->Thu permutation on the wrong day of the week they would end up with a jumble of letters that they would need to undo using a Thu->Wed permutation. Mathematicians would call this the inverse of Wed->Thu.
They could do this in two-line notation by swapping the top and bottom lines and calculating the one-line notation result.
Wed->Thu: 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -------------------------------------------- Thu->Wed: 1 8 11 2 14 6 3 7 12 15 10 13 9 4 5
(To perform the calculation: The 1 in the top line has a 1 below it, so 1 goes in the first position in the result. The 2 in the second line has an 4 above it, so write 2 in the fourth position in the result. Th 3 in the second line has a seven above it, so write 3 in the seventh position in the result and so on.)
Or they could use cycle notation. To invert a cycle, reverse the order of the numbers in it. To maintain the convention that cycles start with the smallest number in the cycle, move the first number in the cycle to the end before reversing. So (9 13 12) -> (13 12 9) -> (9 12 13). Do this for every cycle.
Wed->Thu: (2 8 7 3 11 10 15 5 14 4) (9 12 13) Thu->Wed: (2 4 14 5 15 10 11 3 7 8) (9 13 12)
If Alf and Betty went to visit Anna and Graham on a Wednesday and came home on a Friday, they'd need to figure out the permutation Wed->Fri from the permutations Wed->Thu and Thu->Fri. Mathematicians call this either composition or multiplication, and if A is the notation for Wed->Thu and B is the notation for Thu->Fri, they would write it as BA or B∙A. It may seem backwards, but that's they way mathematicians roll.
Note that B∙A will NOT give the same result as A∙B – unlike regular multiplication of numbers, this sort of multiplication is generally not commutative. (There is an exception to this rule; when two or more cycles are disjoint they can be performed in any order.)
The cycle notation for Thu->Fri is
Thu->Fri: (1 10 4 13 2 8 9 11 3 6) (5 7) (12 14)
and the multiplication Thu->Fri∙Wed->Thu gives the result
Wed->Fri: (1 10 15 7 6 ) (2 9 14 13 11 4 8 5 12)
The order of a permutation is the number of times it needs to be applied for the items being rearranged to return to their starting position, and the signature of a permutation is 1 if an even number of transpositions would be required to do the permutation, and -1 if it required an odd number of permutations.
The order of a permutation is the least common multiple of its cycles' lengths, and the signature is 1 if a permutation has an even number of cycles with an even number of elements, and -1 otherwise.
For a summary of the mathematics discussed here, and a demonstration of the manual method for multiplying permutations in cycle notation, I suggest the Socratica YouTube video Cycle Notation of Permutations - Abstract Algebra.
(Other suitable videos are also available, including the first three parts of this YouTube playlist by Dr Angela Berardinelli.)
Wolfram Alpha is useful resource for testing code. If you enter one-line notation wrapped in parentheses and scroll down a little way when it has finished computing, you will find, amongst other things, the cycle decomposition and the inverse permutation. If you enter cycle notation preceded by the word "permutation" it will give the result of multiplying the cycles in all the notations mentioned above as well as the order and signature of the result.
- Task
Notes:
Alf and Betty chose to represent one-line notation as space delimited numbers without enclosing parentheses, brackets or braces. This representation is not mandated. If it is more convenient to, for example, use comma delimitation and enclosing braces, then do so. Similar considerations apply to their choice of representations for cycles and the cycles of a permutation. State which representations your solution uses.
Their choice of orderings for cycle notation (i.e. smallest number first for cycles, cycles sorted in ascending order by first number) is not mandated. If your solution uses a different ordering, describe it.
Similarly, right-to-left evaluation of cycle multiplication as composition of functions is not mandated. Show how Thu->Fri∙Wed->Thu would be written in your solution.
Alf and Betty's system is one-based. If a zero-based system is more appropriate, then use that, but provide the means (e.g. one or more functions, procedures, subroutines, methods, or words, et cetera) to allow conversion to and from zero-based and one-based representations so that user I/O can be one-based. State if this is the case in your solution.
Their choice of orderings for cycle notation (i.e. smallest number first for cycles, cycles sorted in ascending order by first number) is not mandated. If your solution uses a different ordering, describe it.
Their choice of omitting trailing 1-cycles in one-line notation and all 1-cycles in cycle-notation is not mandated. Include 1-cycles in either notation if you prefer.
Other names exist for some of the terms used in this task. For example, the signature is also known as the sign or parity. Use whichever terms you are comfortable with, but make it clear what they mean.
Data validation is not required for this task. You can assume that all arguments and user inputs are valid. If you do include sanity checks, they should not be to the detriment of the legibility of your code.
If the required functionality is available as part of your language or in a library well known to the language's user base, state this and consider writing equivalent code for bonus points.
- Provide routines (i.e. functions, procedures, subroutines, methods, words or whatever your language uses) that
- given two strings containing the same characters as one another, and without repeated characters within the strings, returns the permutation in either one-line or cycle notation that transforms one of the strings into the other.
- given a permutation, returns the inverse permutation, for both cycle and one-line notation.
- given a permutation and a string of unique characters, returns the string with the characters permuted, for both cycle and one-line notation.
- given two permutations A and B in cycle notation, returns a single permutation in cycle notation equivalent to applying first A and then B. i.e. A.B
- convert permutations in one-line notation to cycle notation and vice versa.
- return the order of a permutation.
- return the signature of a permutation.
- Demonstrate how Alf and Betty can use this functionality in the scenario described above. Assume that Alf and Betty are novice programmers that have some famiiarity with your language. (i.e. Keep It Simple.) Provide the output from your demonstation code.
C++
Please note that the definitions used by Alf and Betty are not in agreement with standard mathematical practice.
#include <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <numeric>
#include <string>
#include <unordered_set>
#include <vector>
typedef std::vector<uint32_t> One_line;
typedef std::vector<One_line> Cycles;
class Permutation {
public:
// Initialise the length of the strings to be permuted.
Permutation(uint32_t letters_size) : letters_count(letters_size) { }
// Return the permutation in one line form that transforms the string 'source' into the string 'destination'.
const One_line create_one_line(const std::string& source, const std::string& destination) {
One_line result;
for ( const char& ch : destination ) {
result.emplace_back(source.find(ch) + 1);
}
while ( result.back() == result.size() ) {
result.pop_back();
}
return result;
}
// Return the cycles of the permutation given in one line form.
const Cycles one_line_to_cycles(One_line& one_line) {
Cycles cycles;
std::unordered_set<uint32_t> used;
for ( uint32_t number = 1; used.size() < one_line.size(); ++number ) {
if ( std::find(used.begin(), used.end(), number) == used.end() ) {
uint32_t index = index_of(one_line, number) + 1;
if ( index > 0 ) {
One_line cycle;
cycle.emplace_back(number);
while ( number != index ) {
cycle.emplace_back(index);
index = index_of(one_line, index) + 1;
}
if ( cycle.size() > 1 ) {
cycles.emplace_back(cycle);
}
used.insert(cycle.begin(), cycle.end());
}
}
}
return cycles;
}
// Return the one line notation of the permutation given in cycle form.
const One_line cycles_to_one_line(Cycles& cycles) {
One_line one_line(letters_count);
std::iota(one_line.begin(), one_line.end(), 1);
for ( uint32_t number = 1; number <= letters_count; ++number ) {
for ( One_line& cycle : cycles ) {
const int32_t index = index_of(cycle, number);
if ( index >= 0 ) {
one_line[number - 1] = cycle[( cycle.size() + index - 1 ) % cycle.size()];
break;
}
}
}
return one_line;
}
// Return the inverse of the given permutation in cycle form.
const Cycles cycles_inverse(const Cycles& cycles) {
Cycles cycles_inverse = cycles;
for ( One_line& cycle : cycles_inverse ) {
cycle.emplace_back(cycle.front());
cycle.erase(cycle.begin());
std::reverse(cycle.begin(), cycle.end());
}
return cycles_inverse;
}
// Return the inverse of the given permutation in one line notation.
const One_line one_line_inverse(One_line& one_line) {
One_line one_line_inverse(one_line.size(), 0);
for ( uint32_t number = 1; number <= one_line.size(); ++number ) {
one_line_inverse[number - 1] = index_of(one_line, number) + 1;
}
return one_line_inverse;
}
// Return the cycles obtained by composing cycleOne first followed by cycleTwo.
const Cycles combined_cycles(Cycles cycles_one, Cycles cycles_two) {
Cycles combined_cycles;
std::unordered_set<uint32_t> used;
for ( uint32_t number = 1; used.size() < letters_count; ++number ) {
if ( std::find(used.begin(), used.end(), number) == used.end() ) {
uint32_t combined = next(cycles_two, next(cycles_one, number));
One_line cycle;
cycle.emplace_back(number);
while ( number != combined ) {
cycle.emplace_back(combined);
combined = next(cycles_two, next(cycles_one, combined));
}
if ( cycle.size() > 1 ) {
combined_cycles.emplace_back(cycle);
}
used.insert(cycle.begin(), cycle.end());
}
}
return combined_cycles;
}
// Return the given string permuted by the permutation given in one line form.
const std::string one_line_permute_string(const std::string text, const One_line one_line) {
std::string permuted;
for ( const uint32_t& index : one_line ) {
permuted.append(1, text[index - 1]);
}
permuted.append(text, permuted.size());
return permuted;
}
// Return the given string permuted by the permutation given in cycle form.
const std::string cycles_permute_string(const std::string& text, Cycles& cycles) {
std::string permuted = text;
for ( const One_line& cycle : cycles ) {
for ( const uint32_t number : cycle ) {
permuted[next(cycles, number) - 1] = text[number - 1];
}
}
return permuted;
}
// Return the signature of the permutation given in one line form.
const std::string signature(One_line one_line) {
Cycles cycles = one_line_to_cycles(one_line);
uint32_t evenCount = 0;
for ( const One_line& cycle : cycles ) {
if ( cycle.size() % 2 == 0 ) {
evenCount++;
}
}
return ( evenCount % 2 == 0 ) ? "+1" : "-1";
}
// Return the order of the permutation given in one line form.
const uint32_t order(One_line one_line) {
Cycles cycles = one_line_to_cycles(one_line);
uint32_t lcm = 1;
for ( const One_line& cycle : cycles ) {
const uint32_t size = cycle.size();
lcm *= size / std::gcd(size, lcm);
}
return lcm;
}
private:
// Return the index of the given digit in the given vector.
const int32_t index_of(One_line& numbers, const uint32_t& digit) {
One_line::iterator iterator = std::find(numbers.begin(), numbers.end(), digit);
return ( iterator == numbers.end() ) ? -1 : std::distance(numbers.begin(), iterator);
}
// Return the element to which the given number is mapped by the permutation given in cycle form.
const uint32_t next(Cycles& cycles, const uint32_t& number) {
for ( One_line& cycle : cycles ) {
if ( std::find(cycle.begin(), cycle.end(), number) != cycle.end() ) {
return cycle[( index_of(cycle, number) + 1 ) % cycle.size()];
}
}
return number;
}
const uint32_t letters_count;
};
std::string to_string(const One_line& one_line) {
std::string result = "(";
for ( const uint32_t& number : one_line ) {
result += std::to_string(number) + " ";
}
return result.substr(0, result.length() - 1) + ") ";
}
std::string to_string(const Cycles& cycles) {
std::string result = "";
for ( const One_line& cycle : cycles ) {
result += to_string(cycle);
}
return result;
}
int main() {
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };
const std::vector<std::string> day_names =
{ "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY" };
const std::vector<std::string> letters = { "HANDYCOILSERUPT", "SPOILUNDERYACHT", "DRAINSTYLEPOUCH",
"DITCHSYRUPALONE", "SOAPYTHIRDUNCLE", "SHINEPARTYCLOUD", "RADIOLUNCHTYPES" };
auto previous_day = [day_names] (const uint32_t& today) {
return ( day_names.size() + today - 1 ) % day_names.size();
};
Permutation permutation(letters[0].length());
std::cout << "On Thursdays Alf and Betty should rearrange their letters using these cycles:" << std::endl;
One_line one_line_wed_thu = permutation.create_one_line(letters[WEDNESDAY], letters[THURSDAY]);
Cycles cycles_wed_thu = permutation.one_line_to_cycles(one_line_wed_thu);
std::cout << to_string(cycles_wed_thu) << std::endl;
std::cout << "So that " << letters[WEDNESDAY] << " becomes " << letters[THURSDAY] << std::endl;
std::cout << "\n" << "Or they could use the one line notation:" << std::endl;
std::cout << to_string(one_line_wed_thu) << std::endl;
std::cout << "\n" << "To revert to the Wednesday arrangement they should use these cycles:" << std::endl;
Cycles cycles_thu_wed = permutation.cycles_inverse(cycles_wed_thu);
std::cout << to_string(cycles_thu_wed) << std::endl;
std::cout << "\n" << "Or with the one line notation:" << std::endl;
One_line one_line_thu_wed = permutation.one_line_inverse(one_line_wed_thu);
std::cout << to_string(one_line_thu_wed) << std::endl;
std::cout << "So that " << letters[THURSDAY] << " becomes "
<< permutation.one_line_permute_string(letters[THURSDAY], one_line_thu_wed) << std::endl;
std::cout << "\n" << "Starting with the Sunday arrangement and applying each of the daily" << std::endl;
std::cout << "arrangements consecutively, the arrangements will be:" << std::endl;
std::cout << "\n" << std::string(6, ' ') << letters[SUNDAY] << "\n" << std::endl;
for ( uint32_t today = 0; today < day_names.size(); ++today ) {
One_line day_one_line = permutation.create_one_line(letters[previous_day(today)], letters[today]);
std::cout << std::setw(11) << day_names[today] << ": "
<< permutation.one_line_permute_string(letters[previous_day(today)], day_one_line)
<< ( ( day_names[today] == "SATURDAY" ) ? "\n" : "" ) << std::endl;
}
std::cout << "\n" << "To go from Wednesday to Friday in a single step they should use these cycles:" << std::endl;
One_line one_line_thu_fri = permutation.create_one_line(letters[THURSDAY], letters[FRIDAY]);
Cycles cycles_thu_fri = permutation.one_line_to_cycles(one_line_thu_fri);
Cycles cycles_wed_fri = permutation.combined_cycles(cycles_wed_thu, cycles_thu_fri);
std::cout << to_string(cycles_wed_fri) << std::endl;
std::cout << "So that " << letters[WEDNESDAY] << " becomes "
<< permutation.cycles_permute_string(letters[WEDNESDAY], cycles_wed_fri) << std::endl;
std::cout << "\n" << "These are the signatures of the permutations:" << "\n" << std::endl;
for ( uint32_t today = 0; today < day_names.size(); ++today ) {
One_line one_line = permutation.create_one_line(letters[previous_day(today)], letters[today]);
std::cout << std::setw(11) << day_names[today] << ": " << permutation.signature(one_line) << std::endl;
}
std::cout << "\n" << "These are the orders of the permutations:" << "\n" << std::endl;
for ( uint32_t today = 0; today < day_names.size(); ++today ) {
One_line one_line = permutation.create_one_line(letters[previous_day(today)], letters[today]);
std::cout << std::setw(11) << day_names[today] << ": " << permutation.order(one_line) << std::endl;
}
std::cout << "\n" << "Applying the Friday cycle to a string 10 times:" << std::endl;
std::string previous = "STOREDAILYPUNCH";
std::cout << "\n" << " 0 " << previous << "\n" << std::endl;
for ( uint32_t i = 1; i <= 10; ++i ) {
previous = permutation.cycles_permute_string(previous, cycles_thu_fri);
std::cout << std::setw(2) << i << " " << previous << ( ( i == 9 ) ? "\n" : "" ) << std::endl;
}
}
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: (2 8 7 3 11 10 15 5 14 4) (9 12 13) So that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE Or they could use the one line notation: (1 4 7 14 15 6 8 2 13 11 3 9 12 5 10) To revert to the Wednesday arrangement they should use these cycles: (2 4 14 5 15 10 11 3 7 8) (9 13 12) Or with the one line notation: (1 8 11 2 14 6 3 7 12 15 10 13 9 4 5) So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily arrangements consecutively, the arrangements will be: RADIOLUNCHTYPES MONDAY: HANDYCOILSERUPT TUESDAY: SPOILUNDERYACHT WEDNESDAY: DRAINSTYLEPOUCH THURSDAY: DITCHSYRUPALONE FRIDAY: SOAPYTHIRDUNCLE SATURDAY: SHINEPARTYCLOUD SUNDAY: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: (1 10 15 7 6) (2 9 14 13 11 4 8 5 12) So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: MONDAY: -1 TUESDAY: -1 WEDNESDAY: +1 THURSDAY: -1 FRIDAY: -1 SATURDAY: +1 SUNDAY: +1 These are the orders of the permutations: MONDAY: 18 TUESDAY: 30 WEDNESDAY: 12 THURSDAY: 30 FRIDAY: 10 SATURDAY: 33 SUNDAY: 40 Applying the Friday cycle to a string 10 times: 0 STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH
Java
Please note that the definitions used by Alf and Betty are not in agreement with standard mathematical practice.
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public final class CyclesOfAPermutation {
public static void main(String[] args) {
enum Day {
MONDAY("HANDYCOILSERUPT"), TUESDAY("SPOILUNDERYACHT"), WEDNESDAY("DRAINSTYLEPOUCH"),
THURSDAY("DITCHSYRUPALONE"), FRIDAY("SOAPYTHIRDUNCLE"), SATURDAY("SHINEPARTYCLOUD"),
SUNDAY("RADIOLUNCHTYPES");
public Day previous() {
return Objects.requireNonNullElseGet(days.lower(this), days::last);
}
public String letters() {
return letters;
}
private Day(String aLetters) {
letters = aLetters;
}
private String letters;
private static NavigableSet<Day> days = new TreeSet<Day>(EnumSet.allOf(Day.class));
}
Permutation permutation = new Permutation(Day.MONDAY.letters.length());
System.out.println("On Thursdays Alf and Betty should rearrange their letters using these cycles:");
List<Integer> oneLineWedThu = permutation.createOneLine(Day.WEDNESDAY.letters(), Day.THURSDAY.letters());
List<List<Integer>> cyclesWedThu = permutation.oneLineToCycles(oneLineWedThu);
System.out.println(cyclesWedThu);
System.out.println("So that " + Day.WEDNESDAY.letters() + " becomes " + Day.THURSDAY.letters());
System.out.println("\n" + "Or they could use the one line notation:");
System.out.println(oneLineWedThu);
System.out.println("\n" + "To revert to the Wednesday arrangement they should use these cycles:");
List<List<Integer>> cyclesThuWed = permutation.cyclesInverse(cyclesWedThu);
System.out.println(cyclesThuWed);
System.out.println("\n" + "Or with the one line notation:");
List<Integer> oneLineThuWed = permutation.oneLineInverse(oneLineWedThu);
System.out.println(oneLineThuWed);
System.out.println("So that " + Day.THURSDAY.letters() + " becomes "
+ permutation.oneLinePermuteString(Day.THURSDAY.letters(), oneLineThuWed));
System.out.println("\n" + "Starting with the Sunday arrangement and applying each of the daily");
System.out.println("arrangements consecutively, the arrangements will be:");
System.out.println("\n" + " ".repeat(6) + Day.SUNDAY.letters() + "\n");
for ( Day day : Day.values() ) {
List<Integer> dayOneLine = permutation.createOneLine(day.previous().letters(), day.letters());
String result = permutation.oneLinePermuteString(day.previous().letters(), dayOneLine);
System.out.println(
String.format("%11s%s%s", day.name() + ": ", result, ( day == Day.SATURDAY ) ? "\n" : ""));
}
System.out.println("\n" + "To go from Wednesday to Friday in a single step they should use these cycles:");
List<Integer> oneLineThuFri = permutation.createOneLine(Day.THURSDAY.letters(), Day.FRIDAY.letters());
List<List<Integer>> cyclesThuFri = permutation.oneLineToCycles(oneLineThuFri);
List<List<Integer>> cyclesWedFri = permutation.combinedCycles(cyclesWedThu, cyclesThuFri);
System.out.println(cyclesWedFri);
System.out.println("So that " + Day.WEDNESDAY.letters() + " becomes "
+ permutation.cyclesPermuteString(Day.WEDNESDAY.letters(), cyclesWedFri));
System.out.println("\n" + "These are the signatures of the permutations:" + "\n");
for ( Day day : Day.values() ) {
List<Integer> oneLine = permutation.createOneLine(day.previous().letters(), day.letters());
System.out.println(String.format("%11s", day.name() + ": ") + permutation.signature(oneLine));
}
System.out.println("\n" + "These are the orders of the permutations:" + "\n");
for ( Day day : Day.values() ) {
List<Integer> oneLine = permutation.createOneLine(day.previous().letters(), day.letters());
System.out.println(String.format("%11s", day.name() + ": ") + permutation.order(oneLine));
}
System.out.println("\n" + "Applying the Friday cycle to a string 10 times:");
String word = "STOREDAILYPUNCH";
System.out.println("\n" + " 0 " + word + "\n");
for ( int i = 1; i <= 10; i++ ) {
word = permutation.cyclesPermuteString(word, cyclesThuFri);
System.out.println(String.format("%2d%s%s%s", i, " ", word, ( i == 9 ) ? "\n" : ""));
}
}
}
final class Permutation {
// Initialise the length of the strings to be permuted.
public Permutation(int aLettersCount) {
lettersCount = aLettersCount;
}
// Return the permutation in one line form that transforms the string 'source' into the string 'destination'.
public List<Integer> createOneLine(String source, String destination) {
List<Integer> result = new ArrayList<Integer>();
for ( char ch : destination.toCharArray() ) {
result.addLast(source.indexOf(ch) + 1);
}
while ( result.getLast() == result.size() ) {
result.removeLast();
}
return result;
}
// Return the cycles of the permutation given in one line form.
public List<List<Integer>> oneLineToCycles(List<Integer> oneLine) {
List<List<Integer>> cycles = new ArrayList<List<Integer>>();
Set<Integer> used = new HashSet<Integer>();
for ( int number = 1; used.size() < oneLine.size(); number++ ) {
if ( ! used.contains(number) ) {
int index = oneLine.indexOf(number) + 1;
if ( index > 0 ) {
List<Integer> cycle = new ArrayList<Integer>();
cycle.addLast(number);
while ( number != index ) {
cycle.addLast(index);
index = oneLine.indexOf(index) + 1;
}
if ( cycle.size() > 1 ) {
cycles.addLast(cycle);
}
used.addAll(cycle);
}
}
}
return cycles;
}
// Return the one line notation of the permutation given in cycle form.
public List<Integer> cyclesToOneLine(List<List<Integer>> cycles) {
List<Integer> oneLine = IntStream.rangeClosed(1, lettersCount).boxed().collect(Collectors.toList());
for ( int number = 1; number <= lettersCount; number++ ) {
for ( List<Integer> cycle : cycles ) {
final int index = cycle.indexOf(number);
if ( index >= 0 ) {
oneLine.set(number - 1, cycle.get(( index - 1 + cycle.size() ) % cycle.size()));
break;
}
}
}
return oneLine;
}
// Return the inverse of the given permutation in cycle form.
public List<List<Integer>> cyclesInverse(List<List<Integer>> cycles) {
List<List<Integer>> cyclesInverse =
cycles.stream().map( list -> new ArrayList<Integer>(list) ).collect(Collectors.toList());
for ( List<Integer> cycle : cyclesInverse ) {
cycle.addLast(cycle.removeFirst());
Collections.reverse(cycle);
}
return cyclesInverse;
}
// Return the inverse of the given permutation in one line notation.
public List<Integer> oneLineInverse(List<Integer> oneLine) {
List<Integer> oneLineInverse = Stream.generate( () -> 0 ).limit(oneLine.size()).collect(Collectors.toList());
for ( int number = 1; number <= oneLine.size(); number++ ) {
oneLineInverse.set(number - 1, oneLine.indexOf(number) + 1);
}
return oneLineInverse;
}
// Return the cycles obtained by composing cycleOne first followed by cycleTwo.
public List<List<Integer>> combinedCycles(List<List<Integer>> cyclesOne, List<List<Integer>> cyclesTwo) {
List<List<Integer>> combinedCycles = new ArrayList<List<Integer>>();
Set<Integer> used = new HashSet<Integer>();
for ( int number = 1; used.size() < lettersCount; number++ ) {
if ( ! used.contains(number) ) {
int combined = next(next(number, cyclesOne), cyclesTwo);
List<Integer> cycle = new ArrayList<Integer>();
cycle.addLast(number);
while ( number != combined ) {
cycle.addLast(combined);
combined = next(next(combined, cyclesOne), cyclesTwo);
}
if ( cycle.size() > 1 ) {
combinedCycles.addLast(cycle);
}
used.addAll(cycle);
}
}
return combinedCycles;
}
// Return the given string permuted by the permutation given in one line form.
public String oneLinePermuteString(String text, List<Integer> oneLine) {
List<String> permuted = new ArrayList<String>();
for ( int index : oneLine ) {
permuted.addLast(text.substring(index - 1, index));
}
permuted.addLast(text.substring(permuted.size()));
return String.join("", permuted);
}
// Return the given string permuted by the permutation given in cycle form.
public String cyclesPermuteString(String text, List<List<Integer>> cycles) {
List<String> permuted = text.chars().mapToObj( i -> String.valueOf((char) i) ).collect(Collectors.toList());
for ( List<Integer> cycle : cycles ) {
for ( int number : cycle ) {
permuted.set(next(number, cycles) - 1, text.substring(number - 1, number));
}
}
return String.join("", permuted);
}
// Return the signature of the permutation given in one line form.
public String signature(List<Integer> oneLine) {
List<List<Integer>> cycles = oneLineToCycles(oneLine);
final long evenCount = cycles.stream().filter( list -> list.size() % 2 == 0 ).count();
return ( evenCount % 2 == 0 ) ? "+1" : "-1";
}
// Return the order of the permutation given in one line form.
public int order(List<Integer> oneLine) {
List<List<Integer>> cycles = oneLineToCycles(oneLine);
List<Integer> sizes = cycles.stream().map( list -> list.size() ).collect(Collectors.toList());
return sizes.stream().reduce(1, (one, two) -> one * ( two / gcd(one, two) ) );
}
// Return the element to which the given number is mapped by the permutation given in cycle form.
private int next(int number, List<List<Integer>> cycles) {
for ( List<Integer> cycle : cycles ) {
if ( cycle.contains(number) ) {
return cycle.get(( cycle.indexOf(number) + 1 ) % cycle.size());
}
}
return number;
}
// Return the greatest common divisor of the two given numbers.
private int gcd(int one, int two) {
return ( two == 0 ) ? one : gcd(two, one % two);
}
private final int lettersCount;
}
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: [[2, 8, 7, 3, 11, 10, 15, 5, 14, 4], [9, 12, 13]] So that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE Or they could use the one line notation: [1, 4, 7, 14, 15, 6, 8, 2, 13, 11, 3, 9, 12, 5, 10] To revert to the Wednesday arrangement they should use these cycles: [[2, 4, 14, 5, 15, 10, 11, 3, 7, 8], [9, 13, 12]] Or with the one line notation: [1, 8, 11, 2, 14, 6, 3, 7, 12, 15, 10, 13, 9, 4, 5] So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily arrangements consecutively, the arrangements will be: RADIOLUNCHTYPES MONDAY: HANDYCOILSERUPT TUESDAY: SPOILUNDERYACHT WEDNESDAY: DRAINSTYLEPOUCH THURSDAY: DITCHSYRUPALONE FRIDAY: SOAPYTHIRDUNCLE SATURDAY: SHINEPARTYCLOUD SUNDAY: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: [[1, 10, 15, 7, 6], [2, 9, 14, 13, 11, 4, 8, 5, 12]] So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: MONDAY: -1 TUESDAY: -1 WEDNESDAY: +1 THURSDAY: -1 FRIDAY: -1 SATURDAY: +1 SUNDAY: +1 These are the orders of the permutations: MONDAY: 18 TUESDAY: 30 WEDNESDAY: 12 THURSDAY: 30 FRIDAY: 10 SATURDAY: 33 SUNDAY: 40 Applying the Friday cycle to a string 10 times: 0 STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH
jq
Works with gojq, the Go implementation of jq
The following is adapted from Wren but the shelf length is not hard-coded in any way. It is, however, assumed throughout that strings are nonempty and consist of distinct upper case letters.
### Generic utility functions
def lpad($len): tostring | ($len - length) as $l | (" " * $l) + .;
def count(s): reduce s as $i (0; . + 1);
# Least common multiple
def lcm($m; $n):
# Define the helper function to take advantage of jq's tail-recursion optimization
def _lcm:
# state is [m, n, i]
if (.[2] % .[1]) == 0 then .[2] else (.[0:2] + [.[2] + $m]) | _lcm end;
[$m, $n, $m] | _lcm;
# Rotate an array $n places to the left assuming 0 <= $n < length
def rotateLeft($n):
.[$n:] + .[:$n];
def days: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
### Cycles of a Permutation
# Arrange a cycle so the least element is first.
def smallestFirst:
. as $cycle
| reduce range(1; length) as $i ( {min: .[0], minIx: 0};
if $cycle[$i] < .min
then .min = $cycle[$i]
| .minIx = $i
end )
| .minIx as $minIx
| $cycle | rotateLeft($minIx) ;
# Converts a list in one-line notation to a space-separated string.
def oneLineToString: join(" ");
# Converts a list in cycle notation to a string where each cycle is space-separated
# and enclosed in parentheses.
def cyclesToString:
map( "(" + join(" ") + ")" ) | "[" + join(" ") + "]";
# Returns a list in one-line notation derived from two equal-length strings s and t.
def oneLineNotation($s; $t):
($s|length) as $len
| reduce range(0; $len) as $i ([]; .[$i] = ($s|index($t[$i:$i+1])) + 1)
| {i: ($len-1), res: .}
| until( .i == -1;
if .res[.i] != .i + 1
then .i=-1
else .i as $i
| .res |= del(.[$i])
| .i += -1
end )
| .res ;
# Returns a list in cycle notation derived from two equal-length strings s and t.
def cycleNotation($s; $t):
{ used: [range(0; $s|length) | false],
cycles: [] }
| reduce range(0; $s|length) as $i (.;
if .used[$i] then .
else .cycle = []
| .used[$i] = true
| .ix = ($t|index($s[$i:$i+1]))
| if $i == .ix then .
else .cycle += [$i+1]
| .done = false
| until (.done;
.cycle += [.ix + 1]
| .used[.ix] = true
| .ix as $j
| .ix = ($t|index($s[$j:$j+1]))
| if .cycle[0] == .ix + 1
then .cycles += [.cycle | smallestFirst ]
| .done = true
else .
end )
end
end )
| .cycles ;
# Converts a list in one-line notation to its inverse
# assuming $targetLength specifies the full length
def oneLineInverse($targetLength):
. as $oneLine
| length as $c
| {s: (map(. + 64) | implode) }
| if $c < $targetLength
then reduce range($c; $targetLength + 1) as $i (.;
.s += ([$c + 65]|implode) )
end
| ([range(0; $targetLength) | . + 65] | implode) as $t
| oneLineNotation(.s; $t) ;
# Converts a list of cycles to its inverse.
def cycleInverse:
map(reverse | smallestFirst) ;
# Permutes input string using perm in one-line notation.
def oneLinePermute($perm):
. as $s
| ($perm|length) as $c
| reduce range(0; $c) as $i ({t:[]};
($perm[$i]-1) as $j
| .t[$i] = $s[$j:$j+1] )
| (.t | join(""))
+ if $c < ($s|length)
then $s[$c:]
else ""
end ;
# Permutes input string in accordance with the permutation specified
# by $cycles
def cyclePermute($cycles):
. as $s
| reduce $cycles[] as $cycle ( [];
reduce range(0;$cycle|length-1) as $i (.;
($cycle[$i]-1) as $j
| .[$cycle[$i+1]-1] = $s[$j:$j+1] )
| ($cycle[-1]-1) as $j
| .[$cycle[0]-1] = $s[$j:$j+1] )
| reduce range(0; $s|length) as $i (.;
if .[$i] == null then .[$i] = $s[$i:$i+1] end)
| join("") ;
# Returns a single permutation in cycle notation resulting from applying
# $cycles1 first and then $cycles2.
def cycleCombine($cycles1; $cycles2; $targetLength):
{s: ([range(0; $targetLength) | . + 65] | implode)}
| .t = (.s | cyclePermute($cycles1))
| .t |= cyclePermute($cycles2)
| cycleNotation(.s; .t);
# Converts a list in one-line notation to cycle notation relative to $targetLength
def oneLineToCycle($targetLength):
. as $oneLine
| length as $c
| { t: ($oneLine | map(. + 64) | implode) }
| if $c < $targetLength
then reduce range($c; $targetLength+1) as $i (.; ## ??
.t += ([$c + 65]|implode) ) ## ??
end
| ([range (0; $targetLength) | . + 65] | implode) as $s
| cycleNotation($s; .t);
# Converts a list in cycle notation to one-line notation.
def cycleToOneLine($targetLength):
. as $cycles
| ([range(0; $targetLength) | . + 65 ] | implode)
| cyclePermute($cycles) as $t
| oneLineNotation(.; $t);
# The order of a permutation specified by its cycles
def order:
def lcm: reduce .[] as $x (1; lcm(.; $x));
map(length) | lcm;
# Returns the signature of a permutation specified by its cycles
def signature:
count(.[] | select(length % 2 == 0))
| if . % 2 == 0 then 1 else -1 end ;
### The task at hand
def letters: [
"HANDYCOILSERUPT", # Monday
"SPOILUNDERYACHT", # Tuesday
"DRAINSTYLEPOUCH", # Wednesday
"DITCHSYRUPALONE", # Thursday
"SOAPYTHIRDUNCLE", # Friday
"SHINEPARTYCLOUD", # Saturday
"RADIOLUNCHTYPES" # Sunday
];
def cycles: cycleNotation(letters[2]; letters[3]);
def prev: "STOREDAILYPUNCH";
cycles
| . as $cycles
| (letters[0] | length) as $targetLength
| cycleToOneLine($targetLength) as $oneLine
| ($oneLine | oneLineInverse($targetLength)) as $oneLine2
| cycleNotation(letters[3]; letters[4]) as $cycles3
| cycleCombine($cycles; $cycles3; $targetLength) as $cycles4
| "On Thursdays Alf and Betty should rearrange their letters using these cycles:",
cyclesToString,
"So that \(letters[2]) becomes \( letters[2] | cyclePermute($cycles) )",
"\nOr they could use the one-line notation:",
($oneLine | oneLineToString),
"\nTo revert to the Wednesday arrangement they should use these cycles:",
(cycleInverse | cyclesToString),
"\nOr with the one-line notation:",
($oneLine2|oneLineToString),
"So that \(letters[3]) becomes \( letters[3] | oneLinePermute($oneLine2))",
"\nStarting with the Sunday arrangement and applying each of the daily arrangements",
"consecutively, the arrangements will be:",
"\n \(letters[6])\n",
(range(0; days|length) as $j
| (if $j == (days|length) - 1 then "" else empty end),
days[$j] + ": " +
( (if $j == 0 then (days|length-1) else $j - 1 end) as $i
| oneLineNotation(letters[$i]; letters[$j]) as $ol
| letters[$i] | oneLinePermute($ol))),
"\nTo go from Wednesday to Friday in a single step they should use these cycles:",
($cycles4|cyclesToString),
"So that \(letters[2]) becomes \( letters[2] | cyclePermute($cycles4))",
"\nThese are the signatures of the permutations:",
(days | join(" ")),
([ range(0; days|length) as $j
| (if $j == 0 then 6 else $j - 1 end) as $i
| (cycleNotation(letters[$i]; letters[$j]) | signature)
| tostring | lpad(3) ] | join(" ")
),
"\nThese are the orders of the permutations:",
(days |join(" ")),
([ range( 0; days|length) as $j
| (if $j == 0 then 6 else $j - 1 end) as $i
| cycleNotation(letters[$i]; letters[$j]) | order]
| map(lpad(3)) | join(" ")),
"\nApplying the Friday cycle to a string 10 times:",
( foreach range (0;11) as $i ( {prev: prev};
.emit = "\($i | lpad(2)) \(.prev)"
| .prev |= cyclePermute($cycles3) )
| .emit )
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: [(2 8 7 3 11 10 15 5 14 4) (9 12 13)] So that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE Or they could use the one-line notation: 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10 To revert to the Wednesday arrangement they should use these cycles: [(2 4 14 5 15 10 11 3 7 8) (9 13 12)] Or with the one-line notation: 1 8 11 2 14 6 3 7 12 15 10 13 9 4 5 So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily arrangements consecutively, the arrangements will be: RADIOLUNCHTYPES Mon: HANDYCOILSERUPT Tue: SPOILUNDERYACHT Wed: DRAINSTYLEPOUCH Thu: DITCHSYRUPALONE Fri: SOAPYTHIRDUNCLE Sat: SHINEPARTYCLOUD Sun: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: [(1 10 15 7 6) (2 9 14 13 11 4 8 5 12)] So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: Mon Tue Wed Thu Fri Sat Sun -1 -1 1 -1 -1 1 1 These are the orders of the permutations: Mon Tue Wed Thu Fri Sat Sun 18 30 12 30 10 33 40 Applying the Friday cycle to a string 10 times: 0 STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH
Julia
Normally, the Permutations.jl Julia package could be used, but equivalent non-library code is used instead, as specified in the task. Note that the Alf and Betty's notation for cycles seems inverted compared to the syntax in Permutations.jl. Printing of cycles is therefore customizable in the code below so as to fit to either format, and the test function's code is specified so as to duplicate the cycles as written in examples given in the task.
""" A Perm is a permutation in one-line form. `a` is a shuffled gapless 1-based range of Int. """
struct Perm
a::Vector{Int}
function Perm(arr::Vector{Int})
if sort(arr) != collect(1:length(arr))
error("$arr must be permutation of 1-based integer range")
end
return new(arr)
end
end
""" Create a Perm from its cycle vectors """
function Perm(cycles::Vector{Vector{Int}}; addsingles = true)
elements = reduce(vcat, cycles)
if addsingles
for elem in filter(x -> !(x in elements), 1:maximum(elements))
push!(cycles, [elem])
push!(elements, elem)
end
end
a = collect(1:length(elements))
sort!(elements) != a && error("Invalid cycles <$cycles> for creating a Perm")
for c in cycles
len = length(c)
for i in 1:len
j, n = c[i], c[mod1(i + 1, len)]
a[j] = n
end
end
return Perm(a)
end
""" length of Perm """
Base.length(p::Perm) = length(p.a)
""" permutation signage for the Perm """
Base.sign(p::Perm) = iseven(sum(c -> iseven(length(c)), permcycles(p))) ? 1 : -1
""" order of permutation for Perm """
order(p::Perm) = lcm(map(length, permcycles(p)))
""" Composition of Perm permutations with the * operator """
function Base.:*(p1:: Perm, p2::Perm)
len = length(p1)
len != length(p2) && error("Permutations must be of same length")
return Perm([p1.a[p2.a[i]] for i in 1:len])
end
""" inverse of a Perm """
function Base.inv(p::Perm)
a = zeros(Int, length(p))
for i in 1:length(p)
j = p.a[i]
a[j] = i
end
return Perm(a)
end
""" Get cycles of a Perm permutation as a vector of integer vectors, optionally with singles """
function permcycles(p::Perm; includesingles = false)
pdict, cycles = Dict(enumerate(p.a)), Vector{Int}[]
for i in 1:length(p.a)
if (j = pop!(pdict, i, 0)) != 0
c = [i]
while i != j
push!(c, j)
j = pop!(pdict, j)
end
push!(cycles, c)
end
end
return includesingles ? cycles : filter(c -> length(c) > 1, cycles)
end
""" Perm prints in cycle or optionally oneline format """
function Base.print(io::IO, p::Perm; oneline = false, printsinglecycles = false, AlfBetty = false)
if length(p) == 0
print(io, "()")
end
if oneline
width = length(string(maximum(p.a))) + 1
print(io, "[ " * prod(map(n -> "$n ", p.a)) * "]")
else
cycles = permcycles(AlfBetty ? inv(p) : p, includesingles = printsinglecycles)
print(io, prod(c -> "(" * string(c)[begin+1:end-1] * ") ", cycles))
end
end
""" Create a Perm from a string with only one of each of its letters """
Perm(s::AbstractString) = Perm([findfirst(==(c), String(sort(unique(collect(s))))) for c in s])
""" Create a Perm from two strings permuting first string to the second one """
Perm(s1::AbstractString, s2::AbstractString) = Perm([findfirst(==(c), s1) for c in s2])
""" Create a permuted string from another string using a Perm """
permutestring(s::AbstractString, p::Perm) = String([s[i] for i in p.a])
function testAlfBettyPerms()
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
daystrings = ["HANDYCOILSERUPT", "SPOILUNDERYACHT", "DRAINSTYLEPOUCH",
"DITCHSYRUPALONE", "SOAPYTHIRDUNCLE", "SHINEPARTYCLOUD", "RADIOLUNCHTYPES"]
dayperms = [Perm(daystrings[mod1(i - 1, 7)], daystrings[i]) for i in 1:length(days)]
print("On Thursdays Alf and Betty should rearrange\ntheir letters using these cycles: ")
print(stdout, dayperms[4], AlfBetty = true)
println("\n\nSo that $(daystrings[3]) becomes $(daystrings[4])")
print("\nor they could use the one-line notation: ")
print(stdout, dayperms[4]; oneline = true)
print("\n\n\nTo revert to the Wednesday arrangement they\nshould use these cycles: ")
print(stdout, inv(dayperms[4]), AlfBetty = true)
print("\n\nor with the one-line notation: " )
print(stdout, inv(dayperms[4]); oneline = true)
println("\n\nSo that $(daystrings[4]) becomes $(daystrings[3])")
println("\n\nStarting with the Sunday arrangement and applying each of the daily")
println("permutations consecutively, the arrangements will be:\n")
println(" "^6, daystrings[7], "\n")
for i in 1:length(days)
i == 7 && println()
println(days[i], ": ", permutestring(daystrings[mod1(i - 1, 7)], dayperms[i]))
end
Base.println("\n\nTo go from Wednesday to Friday in a single step they should use these cycles: ")
print(stdout, Perm(daystrings[3], daystrings[5]), AlfBetty = true)
println("\n\nSo that $(daystrings[3]) becomes $(daystrings[5])")
println("\n\nThese are the signatures of the permutations:\n\n Mon Tue Wed Thu Fri Sat Sun")
for i in 1:length(days)
j = i == 1 ? length(days) : i - 1
print(lpad(sign(Perm(daystrings[mod1(i - 1, 7)], daystrings[i])), 4))
end
println("\n\nThese are the orders of the permutations:\n\n Mon Tue Wed Thu Fri Sat Sun")
for i in 1:7
print(lpad(order(dayperms[i]), 4))
end
println("\n\nApplying the Friday cycle to a string 10 times:\n")
pFri, str = dayperms[5], "STOREDAILYPUNCH"
println(" $str\n")
for i in 1:10
str = permutestring(str, pFri)
println(lpad(i, 2), " ", str, i == 9 ? "\n" : "")
end
end
testAlfBettyPerms()
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: (2, 8, 7, 3, 11, 10, 15, 5, 14, 4) (9, 12, 13) So that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE or they could use the one-line notation: [ 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10 ] To revert to the Wednesday arrangement they should use these cycles: (2, 4, 14, 5, 15, 10, 11, 3, 7, 8) (9, 13, 12) or with the one-line notation: [ 1 8 11 2 14 6 3 7 12 15 10 13 9 4 5 ] So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily permutations consecutively, the arrangements will be: RADIOLUNCHTYPES Mon: HANDYCOILSERUPT Tue: SPOILUNDERYACHT Wed: DRAINSTYLEPOUCH Thu: DITCHSYRUPALONE Fri: SOAPYTHIRDUNCLE Sat: SHINEPARTYCLOUD Sun: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: (1, 10, 15, 7, 6) (2, 9, 14, 13, 11, 4, 8, 5, 12) So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: Mon Tue Wed Thu Fri Sat Sun -1 -1 1 -1 -1 1 1 These are the orders of the permutations: Mon Tue Wed Thu Fri Sat Sun 18 30 12 30 10 33 40 Applying the Friday cycle to a string 10 times: STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH
Nim
import std/[algorithm, math, sequtils, setutils, strutils, sugar, tables]
type
Perm* = object
a: seq[int]
Cycle* = seq[int]
template isEven(n: int): bool = (n and 1) == 0
func newPerm*(len: Natural = 0): Perm =
## Create a Perm with given length.
result.a = newSeq[int](len)
func newPerm*(a: openArray[int]): Perm =
## Create a Perm with given values.
assert sorted(a) == toSeq(1..a.len), "Perm should be a shuffled 1-based range"
result.a = a.toSeq
template len*(perm: Perm): int = perm.a.len
template `[]`*(perm: Perm; idx: Natural): int =
## Return the element at given one base index.
perm.a[idx - 1]
template `[]=`*(perm: var Perm; idx: Natural; val: int) =
## Set the element at given one base index.
perm.a[idx - 1] = val
iterator pairs*(perm: Perm): (int, int) =
## Yield the couples (one-based-index, val).
for i in 1..perm.len:
yield (i, perm[i])
func inv*(perm: Perm): Perm =
## Return the inverse of a Perm.
result = newPerm(perm.len)
for i in 1..perm.len:
let j = perm[i]
result[j] = i
func cycles*(perm: Perm; includeSingles = false): seq[Cycle] =
## Get cycles of a Perm permutation as a sequence of integer
## sequences, optionally with singles.
var ptable = collect(for (i, val) in perm.pairs: {i: val})
for i in 1..perm.len:
var j: int
if ptable.pop(i, j):
var c = @[i]
while i != j:
c.add j
discard ptable.pop(j, j)
if includeSingles or c.len > 1:
result.add c
func cycleFormat*(perm: Perm; alfBettyForm = false): string =
## Stringify the Perm as its cycles, optionally in Rosetta Code task format.
let p = if alfBettyForm: inv(perm) else: perm
let cyclestrings = collect:
for c in p.cycles:
'(' & c.join(" ") & ')'
result = cycleStrings.join(" ")
func oneLineFormat*(perm: Perm): string =
## Stringify the Perm in one-line permutation format.
result = "[ " & perm.a.join(" ") & " ]"
func sign*(perm: Perm): int =
## Return the sign of the permutation.
var sum = 0
for c in perm.cycles:
sum += ord(c.len.isEven)
result = if sum.isEven: 1 else: -1
func order*(perm: Perm): int =
## Return the order of permutation for Perm.
lcm(perm.cycles.mapIt(it.len))
func `*`*(p1, p2: Perm): Perm =
## Return the composition of Perm permutations with the * operator.
assert p1.len == p2.len, "permutations must be of same length"
result = newPerm(collect(for i in 1..p1.len: p1[p2[i]]))
func toPerm*(cycles: seq[Cycle]; addSingles = true): Perm =
## Create a Perm from a sequence of cycles.
var cycles = cycles
var elements = collect:
for c in cycles:
for e in c: e
let allPossible = toSeq(1..elements.len)
if addSingles:
let missings = collect:
for x in allPossible:
if x notin elements: x
for elem in missings:
cycles.add @[elem]
elements.add elem
assert sorted(elements) == allPossible, "invalid cycles for creating a Perm"
result = newPerm(elements.len)
for c in cycles:
let length = c.len
for i in 1..length:
let j = c[i]
let n = c[(i + 1) mod length]
result[j] = n
func toPerm*(s: string): Perm =
## Create a Perm from a string with only one of each of its letters.
let letters = sorted(s).deduplicate(true)
result = newPerm(collect(for c in s: letters.find(c) + 1))
func toPerm*(s1, s2: string): Perm =
## Create a Perm from two strings permuting first string to the second one.
result = newPerm(collect(for c in s2: s1.find(c) + 1))
func permutedString*(s: string; p: Perm): string =
## Create a permuted string from another string using Perm p.
collect(for i in p.a: s[i - 1]).join("")
when isMainModule:
let
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
dayStrings = ["HANDYCOILSERUPT", "SPOILUNDERYACHT", "DRAINSTYLEPOUCH",
"DITCHSYRUPALONE", "SOAPYTHIRDUNCLE", "SHINEPARTYCLOUD", "RADIOLUNCHTYPES"]
dayPerms = collect:
for i in 0..6:
(toPerm(dayStrings[(i + 6) mod 7], dayStrings[i]))
echo "On Thursdays Alf and Betty should rearrange"
echo "their letters using these cycles: ", dayPerms[3].cycleFormat(true), '\n'
echo "So that ", dayStrings[2], " becomes ", daystrings[3], '\n'
echo "or they could use the one-line notation: ", dayPerms[3].oneLineFormat(), "\n\n"
echo "To revert to the Wednesday arrangement they"
echo "should use these cycles: ", inv(dayPerms[3]).cycleformat(true), '\n'
echo "or with the one-line notation: ", inv(dayperms[3]).oneLineFormat(), '\n'
echo "So that ", dayStrings[3], " becomes ", dayStrings[2], "\n\n"
echo "Starting with the Sunday arrangement and applying each of the daily"
echo "permutations consecutively, the arrangements will be:\n\n ", dayStrings[6]
for i in 0..6:
if i == 6: echo()
echo days[i], ": ", permutedString(daystrings[(i + 6) mod 7], dayPerms[i])
echo "\n\nTo go from Wednesday to Friday in a single step they should use these cycles: "
echo toPerm(dayStrings[2], dayStrings[4]).cycleformat(true), '\n'
echo "So that ", dayStrings[2], " becomes ", dayStrings[4], "\n\n"
echo "These are the signatures of the permutations:\n"
echo " Mon Tue Wed Thu Fri Sat Sun"
for i in 0..6:
stdout.write align($toPerm(dayStrings[(i + 6) mod 7], daystrings[i]).sign, 4)
echo "\n\nThese are the orders of the permutations:\n"
echo " Mon Tue Wed Thu Fri Sat Sun"
for i in 0..6:
stdout.write align($dayPerms[i].order, 4)
echo "\n\nApplying the Friday cycle to a string 10 times:\n"
let pFri = dayperms[4]
var spe = "STOREDAILYPUNCH"
echo " ", spe
for i in 1..10:
spe = permutedString(spe, pFri)
echo align($i, 2), ' ', spe, if i == 9: "\n" else: ""
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: (2 8 7 3 11 10 15 5 14 4) (9 12 13) So that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE or they could use the one-line notation: [ 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10 ] To revert to the Wednesday arrangement they should use these cycles: (2 4 14 5 15 10 11 3 7 8) (9 13 12) or with the one-line notation: [ 1 8 11 2 14 6 3 7 12 15 10 13 9 4 5 ] So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily permutations consecutively, the arrangements will be: RADIOLUNCHTYPES Mon: HANDYCOILSERUPT Tue: SPOILUNDERYACHT Wed: DRAINSTYLEPOUCH Thu: DITCHSYRUPALONE Fri: SOAPYTHIRDUNCLE Sat: SHINEPARTYCLOUD Sun: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: (1 10 15 7 6) (2 9 14 13 11 4 8 5 12) So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: Mon Tue Wed Thu Fri Sat Sun -1 -1 1 -1 -1 1 1 These are the orders of the permutations: Mon Tue Wed Thu Fri Sat Sun 18 30 12 30 10 33 40 Applying the Friday cycle to a string 10 times: STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH
Phix
with javascript_semantics requires("1.0.2") -- (tagstart) function smallest_first(sequence cycle) -- Rearrange a cycle so the lowest element is first. integer mdx = smallest(cycle,true) return cycle[mdx..$] & cycle[1..mdx-1] end function function one_line_to_string(sequence one_line) -- Converts a list in one line notation to a space separated string. return join(one_line," ",fmt:="%d") end function function cycles_to_string(sequence cycles) -- Converts a list in cycle notation to a string where each cycle -- is space separated and enclosed in parentheses. return join(apply(true,join,{cycles,{" "},{" "},{"%d"}}),fmt:="(%s)") end function function one_line_notation(sequence s, t) -- Returns a list in one line notation derived from two strings s and t. integer l = length(s) sequence res = repeat(0,l) for i=1 to l do res[i] = find(t[i],s) end for for i=l to 1 by -1 do if res[i]!=i then exit end if res = res[1..-2] end for return res end function function cycle_notation(string s, t) -- Returns a list in cycle notation derived from two strings s and t. sequence used = repeat(false,length(s)), cycles = {} for i=1 to length(used) do if not used[i] then used[i] = true integer ix = find(s[i],t) if ix!=i then sequence cycle = {i} while true do cycle &= ix used[ix] = true ix = find(s[ix],t) if ix=i then cycle = smallest_first(cycle) cycles = append(cycles,cycle) exit end if end while end if end if end for return cycles end function function one_line_inverse(sequence one_line) -- Converts a list in one line notation to its inverse. integer l = length(one_line) string s = repeat(' ',l), t = tagstart('A',l) for i=1 to l do s[i] = one_line[i]+'A'-1 end for return one_line_notation(s,t) end function function cycle_inverse(sequence cycles) -- Converts a list of cycles to its inverse. sequence cycles2 = repeat(0,length(cycles)) for i=1 to length(cycles) do sequence cycle = cycles[i] cycles2[i] = cycle[1] & reverse(cycle[2..$]) end for return cycles2 end function function one_line_permute(string s, sequence perm) -- Permutes a string using perm in one line notation. string t = s for i=1 to length(perm) do t[i] = s[perm[i]] end for return t --alternative one-liner: -- return reinstate(s,tagset(length(perm)),extract(s,perm)) end function function cycle_permute(string s, sequence cycles) -- Permutes a string using perm in cycle notation. sequence t = s for cycle in cycles do for i=1 to length(cycle)-1 do t[cycle[i+1]] = s[cycle[i]] end for t[cycle[1]] = s[cycle[$]] end for return t end function function cycle_combine(sequence cycles1, cycles2) // Returns a single perm in cycle notation resulting // from applying cycles1 first and then cycles2. integer m = max(max(flatten(cycles1)), max(flatten(cycles1))) string s = tagstart('A',m), t = cycle_permute(s, cycles1) t = cycle_permute(t, cycles2) return cycle_notation(s, t) end function function one_line_to_cycle(sequence one_line) // Converts a list in one line notation to cycle notation. integer l = length(one_line) string s = tagstart('A',l), t = repeat(' ',l) for i=1 to l do t[i] = one_line[i]+'A'-1 end for return cycle_notation(s, t) end function function cycle_to_one_line(sequence cycles) -- Converts a list in cycle notation to one line notation. string s = tagstart('A',max(flatten(cycles))), t = cycle_permute(s, cycles) return one_line_notation(s, t) end function function cycle_order(sequence cycles) -- Returns the order of a permutation. return lcm(apply(cycles,length)) end function function cycle_signature(sequence cycles) -- Returns the signature of a permutation. return even(sum(apply(apply(cycles,length),even)))*2-1 end function constant DAYS = { MON := "HANDYCOILSERUPT", TUE := "SPOILUNDERYACHT", WED := "DRAINSTYLEPOUCH", THU := "DITCHSYRUPALONE", FRI := "SOAPYTHIRDUNCLE", SAT := "SHINEPARTYCLOUD", SUN := "RADIOLUNCHTYPES" } printf(1,"On Thursdays Alf and Betty should rearrange their letters using these cycles:\n") sequence cycle_wt = cycle_notation(WED,THU) printf(1,"%s\n",{cycles_to_string(cycle_wt)}) printf(1,"So that %s becomes %s\n\n",{WED,cycle_permute(WED,cycle_wt)}) sequence one_line = cycle_to_one_line(cycle_wt) assert(one_line_to_cycle(one_line)==cycle_wt) printf(1,"Or they could use the one line notation:\n%s\n\n",{one_line_to_string(one_line)}) printf(1,"To revert to the Wednesday arrangement they should use these cycles:\n") printf(1,"%s\n\n",{cycles_to_string(cycle_inverse(cycle_wt))}) printf(1,"Or with the one line notation:\n") sequence one_line2 = one_line_inverse(one_line) printf(1,"%s\n",one_line_to_string(one_line2)) printf(1,"So that %s becomes %s\n\n",{THU,one_line_permute(THU,one_line2)}) printf(1,"Starting with the Sunday arrangement and applying each of the daily arrangements\n") printf(1,"consecutively, the arrangements will be:\n\n") printf(1," %s\n\n",{(SUN)}) sequence signatures = {}, orders = {} integer yesterday = length(DAYS) for today=1 to length(DAYS) do string td = DAYS[today], yd = DAYS[yesterday] sequence ol = one_line_notation(yd,td), cy = cycle_notation(yd,td) printf(1,"%s: %s\n",{td,one_line_permute(yd,ol)}) if today>=find(SAT,DAYS) then printf(1,"\n") end if signatures &= cycle_signature(cy) orders &= cycle_order(cy) yesterday := today end for printf(1,"To go from Wednesday to Friday in a single step they should use these cycles:\n") sequence cycles_tf = cycle_notation(THU,FRI), cycles_wf = cycle_combine(cycle_wt,cycles_tf) printf(1,"%s\n",{cycles_to_string(cycles_wf)}) printf(1,"So that %s becomes %s\n\n",{WED,cycle_permute(WED,cycles_wf)}) constant FMT = """ These are the %s of the permutations: Mon Tue Wed Thu Fri Sat Sun %s """ printf(1,FMT,{"signatures",join(signatures,fmt:="%2d ")}) printf(1,FMT,{"orders",join(orders,fmt:="%3d")}) printf(1,"Applying the Friday cycle to a string 10 times:\n\n") string prev = "STOREDAILYPUNCH" printf(1," %s\n\n",{prev}) for i=1 to 10 do prev = cycle_permute(prev,cycles_tf) printf(1,"%2d %s\n",{i,prev}) if i>=9 then printf(1,"\n") end if end for
Output identical to Julia/Wren
Python
""" For Rosetta Code task Cycles_of_a_permutation """
from math import lcm # in python 3.9+
class Perm:
""" 1 -based permutations of range of integers """
def __init__(self, range_or_list):
""" make a perm from a list or range of integers """
self.a = list(range_or_list)
assert sorted(self.a) == list(range(1, len(self.a) + 1)),\
'Perm should be a shuffled 1-based range'
def __repr__(self):
return 'Permutation class Perm'
def cycleformat(self, AlfBettyForm = False):
""" stringify the Perm as its cycles, optionally if Rosetta Code task format """
p = self.inv() if AlfBettyForm else self
cyclestrings = ["(" + " ".join([str(i) for i in c]) + ")" for c in p.cycles()]
return '( ' + ' '.join(cyclestrings) + ' )'
def onelineformat(self):
""" stringify the Perm in one-line permutation format """
return '[ ' + ' '.join([str(i) for i in self.a]) + ' ]'
def len(self):
""" length """
return len(self.a)
def sign(self):
""" sign """
return 1 if sum([len(c) % 2 == 0 for c in self.cycles()]) % 2 == 0 else -1
def order(self):
""" order of permutation for Perm """
return lcm(*[len(c) for c in self.cycles()])
def __mul__(self, p2):
""" Composition of Perm permutations with the * operator """
length = len(self.a)
assert length == len(p2.a), 'Permutations must be of same length'
return Perm([self.a[p2.a[i] - 1] for i in range(len(self.a))])
def inv(self):
""" inverse of a Perm """
length = len(self.a)
newa = [0 for _ in range(length)]
for idx in range(length):
jidx = self.a[idx]
newa[jidx - 1] = idx + 1
return Perm(newa)
def cycles(self, *, includesingles = False):
"""
Get cycles of a Perm permutation as a list of integer lists,
optionally with single cycles included, otherwise omitiing singles
"""
v = self.a
length = len(v)
unchecked = [True] * length
foundcycles = []
for idx in range(length):
if unchecked[idx]:
c = [idx + 1]
unchecked[idx] = False
jidx = idx
while unchecked[v[jidx] - 1]:
jidx = v[jidx]
c.append(jidx)
jidx -= 1
unchecked[jidx] = False
if len(c) > 1 or includesingles:
foundcycles.append(c)
return sorted(foundcycles)
def cycles_to_Perm(cycles, *, addsingles = True):
""" Create a Perm from a vector of cycles """
elements = [e for c in cycles for e in c]
allpossible = list(range(1, len(elements) + 1))
if addsingles:
missings = [x for x in allpossible if not x in elements]
for elem in missings:
cycles.append([elem])
elements.append(elem)
assert sorted(elements) == allpossible, 'Invalid cycles for creating a Perm'
a = [0 for _ in range(len(elements))]
for c in cycles:
length = len(c)
for idx in range(length):
jidx, n = c[idx], c[(idx + 1) % length]
a[jidx - 1] = n
return Perm(a)
def string_to_Perm(s):
""" Create a Perm from a string with only one of each of its letters """
letters = sorted(list(set(list(s))))
return Perm([letters.index(c) + 1 for c in s])
def two_string_to_Perm(s1, s2):
""" Create a Perm from two strings permuting first string to the second one """
return Perm([s1.index(c) + 1 for c in s2])
def permutestring(s, p):
""" Create a permuted string from another string using Perm p """
return ''.join([s[i - 1] for i in p.a])
if __name__ == '__main__':
# Testing code
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
daystrings = ['HANDYCOILSERUPT', 'SPOILUNDERYACHT', 'DRAINSTYLEPOUCH',
'DITCHSYRUPALONE', 'SOAPYTHIRDUNCLE', 'SHINEPARTYCLOUD', 'RADIOLUNCHTYPES']
dayperms = [two_string_to_Perm(daystrings[(i - 1) % 7], daystrings[i]) for i in range(7)]
print('On Thursdays Alf and Betty should rearrange\ntheir letters using these cycles:',
' ', dayperms[3].cycleformat(True), '\n\n\nSo that ', daystrings[2], ' becomes ',
daystrings[3], '\n\nor they could use the one-line notation: ',
dayperms[3].onelineformat(),
'\n\n\n\nTo revert to the Wednesday arrangement they\nshould use these cycles: ',
dayperms[3].inv().cycleformat(True), '\n\n\nor with the one-line notation: ',
dayperms[3].inv().onelineformat(), '\n\n\nSo that ', daystrings[3], ' becomes ',
daystrings[2],
'\n\n\n\nStarting with the Sunday arrangement and applying each of the daily',
'\npermutations consecutively, the arrangements will be:\n\n ',
daystrings[6], '\n')
for i in range(7):
if i == 6:
print()
print(days[i], ': ', permutestring(daystrings[(i - 1) % 7], dayperms[i]))
print('\n\n\nTo go from Wednesday to Friday in a single step they should use these cycles: ')
print(two_string_to_Perm(daystrings[2], daystrings[4]).cycleformat(True))
print('\n\nSo that ', daystrings[2], ' becomes ', daystrings[4])
print('\n\n\nThese are the signatures of the permutations:\n\n Mon Tue Wed Thu Fri Sat Sun')
for i in range(7):
j = 6 if i == 0 else i - 1
print(str(two_string_to_Perm(daystrings[(i - 1) % 7],
daystrings[i]).sign()).rjust(4), end='')
print('\n\n\nThese are the orders of the permutations:\n\n Mon Tue Wed Thu Fri Sat Sun')
for i in range(7):
print(str(dayperms[i].order()).rjust(4), end='')
print("\n\nApplying the Friday cycle to a string 10 times:\n")
PFRI, SPE = dayperms[4], 'STOREDAILYPUNCH'
print(" ", SPE, '\n')
for i in range(10):
SPE = permutestring(SPE, PFRI)
print(str(i+1).rjust(2), ' ', SPE, '\n' if i == 8 else '')
- Output:
Same as Quackery.
Quackery
( Glossary
--------
General Utilities
-----------------
even ( a --> b );
Returns true (1) if the number a is divisible by 2, otherwise
returns false (0).
gcd ( a b --> c );
Returns the number c, the positive greatest common denominator of
the numbers a and b.
lcm ( a b --> c );
Returns the number c, the positive least common multiple of the
numbers a and b.
bump ( a b --> c );
a is any Quackery item. If a is a number, returns a+b. If a is an
operator, bump returns it unchanged. If it is a nest, bump adds b to
every numbers in the nest and applies this recursively to any nested
nests, excluding named nests.
In the context of the task, can be used to switch both one-line and
cyclic permutations (nests of numbers and nests of nests of numbers
respectively) between one-based (if user-preferred) and zero-based
(internal) representations.
Permutation Specific
--------------------
makeperm ( a b --> c );
a and b are nests of items (e.g. strings) with the properties that
both contain the same items, but not necessarily in the same order,
and that there is only one instance of any item within a nest.
References to strings in this glossary should be construed to
include other nests with the properties noted here.
Returns c, the permutation required to transform a into b. C is a
nest of zero-based one-line notation (ZBOLN), with trailing
one-cycles omitted.
identity ( a --> b );
Returns the nest b, containing the numbers 0 to a-1 in ascending
order. This is the an identity permutation in ZBOLN for n items
without one-cycles omitted.
invert ( a --> b );
Takes a, a permutation in ZBOLN, and returns b, the inverse of a.
i.e. if a is the permutation that transforms the string X into the
string Y, b will transform Y into X.)
permute ( a b --> c );
Returns the string c, which is the string a permuted by b, a
permutation in ZBOLN.
decompose ( a --> b );
Takes a permutation in ZBOLN and returns the equivalent permutation
as a nest in zero-based cyclic notation (ZBCN) with one-cycles
omitted. Each cycle is a nest of numbers.
cypersize ( a --> b );
Takes the permutation a in ZBCN and returns the largest number in it
plus 1, i.e. the minimum size of a string that the permutation a can
act on.
cyinvert ( a --> b );
Same as invert, but a and b are in ZBCN rather than ZBOLN.
cypermute ( a b --> c );
Same as permute, but b is a permutation in ZBCN.
cymultiply ( a b --> c );
a, b and c are permutations in ZBCN. c is equal to applying first b
and then a to a string i.e. a⋅b)
recompose ( a --> b );
Takes a permutation in ZBCN and returns the equivalent permutation as
a nest in ZBOLN.
order ( a --> b );
Takes a permutation, a, in ZBCN and returns a number, b, which is the
order of the permutation a.
signature ( a --> b );
Takes a permutation, a, in ZBCN and returns a number, b, which is the
signature (parity) of the permutation a.
Task Specific
-------------
monday tuesday wednesday thursday friday saturday sunday
ALL: ( --> a );
Each of these words returns a number associated with the weekday that
they are named after.
day$ ( a --> b );
Take a day number, a, and returns the abbreviated day name as a
string, b, for display purposes.
anagram ( a --> b );
Takes a day number, a, and returns a string, b, of the letter
arrangement Alf and Betty have chosen for that day.
one-line ( a --> b );
Takes a day number, a, and returns a nest, b, of the permutation that
Alf and Betty need to use on that day in ZBOLN.
cycle ( a --> b );
Takes a day number, a, and returns a nest, b, of the permutation that
Alf and Betty need to use on that day in ZBCN.
)
( General Utilities )
( ------- --------- )
[ 1 & not ] is even ( n --> b )
[ [ dup while tuck mod again ]
drop abs ] is gcd ( n n --> n )
[ 2dup and iff
[ 2dup gcd / * abs ]
else and ] is lcm ( n n --> n )
[ over number? iff
+ done
over [] = iff
drop done
over named? iff
drop done
dip behead tuck recurse
nested unrot recurse
join ] is bump ( x n --> x )
( Permutation Specific )
( ----------- -------- )
[ [] unrot
witheach
[ over find swap dip join ]
drop
dup size times
[ dup i peek i = iff
[ -1 split drop ]
else conclude ] ] is makeperm ( [ [ --> [ )
[ [] swap times [ i^ join ] ] is identity ( n --> [ )
[ 0 over size of
swap witheach
[ i^ unrot poke ] ] is invert ( [ --> [ )
[ dip dup
[ witheach
[ dip dup peek
unrot dip
[ i^ poke ] ] ]
drop ] is permute ( [ [ --> [ )
[ [] swap 0 temp put
dup size times
[ dup i^ peek i^ = if done
i^ bit
temp share & if done
i^ [] unrot
[ dup bit temp take |
temp put
dip dup peek
rot dip dup join unrot
dup bit temp share &
until ]
drop dip [ nested join ] ]
drop
temp release ] is decompose ( [ --> [ )
[ 0 swap witheach
[ witheach max ] 1+ ] is cypersize ( [ --> n )
[ [] swap witheach
[ behead join reverse
nested join ] ] is cyinvert ( [ --> [ )
[ witheach
[ 2dup -1 peek peek unrot
witheach
[ dup dip
[ dip dup peek
unrot ]
poke ]
nip ] ] is cypermute ( [ [ --> [ )
[ 2dup join cypersize identity
swap cypermute
swap cypermute decompose ] is cymultiply ( [ [ --> [ )
[ dup cypersize identity swap cypermute ] is recompose ( [ --> [ )
[ 1 swap witheach [ size lcm ] ] is order ( [ --> n )
[ 0 swap witheach [ size even + ]
even iff 1 else -1 ] is signature ( [ --> n )
( Task Specific )
( ---- -------- )
[ 0 ] is monday ( --> n )
[ 1 ] is tuesday ( --> n )
[ 2 ] is wednesday ( --> n )
[ 3 ] is thursday ( --> n )
[ 4 ] is friday ( --> n )
[ 5 ] is saturday ( --> n )
[ 6 ] is sunday ( --> n )
[ table ] is day$ ( n --> $ )
$ "Mon Tue Wed Thu Fri Sat Sun"
nest$ witheach [ ' day$ put ]
[ table ] is anagram ( n --> $ )
$ "HANDYCOILSERUPT SPOILUNDERYACHT DRAINSTYLEPOUCH "
$ "DITCHSYRUPALONE SOAPYTHIRDUNCLE SHINEPARTYCLOUD " join
$ "RADIOLUNCHTYPES" join
nest$ witheach [ ' anagram put ]
[ table ] is one-line ( n --> [ )
7 times
[ i^ 1 - 7 mod anagram
i^ anagram makeperm
' one-line put ]
[ table ] is cycle ( n --> [ )
7 times [ i^ one-line decompose ' cycle put ]
( Demonstration Code )
( ------------- ---- )
say "On Thursdays Alf and Betty should rearrange" cr
say "their letters using these cycles: "
thursday cycle 1 bump echo cr cr
say "so that "
wednesday anagram echo$
say " becomes "
wednesday anagram
thursday cycle cypermute echo$ cr cr
say "or they could use the one-line notation: "
thursday one-line 1 bump echo cr cr cr
say "To revert to the Wednesday arrangement they" cr
say "should use these cycles: "
thursday cycle cyinvert 1 bump echo cr cr
say "or with the one-line notation: "
thursday one-line invert 1 bump echo cr cr
say "So that "
thursday anagram echo$
say " becomes "
thursday anagram
thursday one-line invert permute echo$ cr cr cr
say "Starting with the Sunday arrangement and" cr
say "applying each of the daily permutations " cr
say "consecutively, the arrangements will be:" cr cr
sunday anagram dup say " " echo$ cr cr
7 times
[ i 0 = if cr
i^ day$ echo$ say ": "
i^ cycle cypermute
dup echo$ cr ]
drop cr cr
say "To go from Wednesday to Friday in a" cr
say "single step they should use these cycles: "
friday cycle
thursday cycle cymultiply 1 bump echo cr cr
say "So that "
wednesday anagram echo$
say " becomes "
friday cycle
thursday cycle cymultiply
wednesday anagram swap cypermute echo$ cr cr cr
say "These are the signatures of the permutations:" cr cr
7 times [ sp i^ day$ echo$ ] cr
7 times
[ i^ cycle signature
dup 0 > if sp
sp echo sp ] cr cr cr
say "These are the orders of the permutations:" cr cr
7 times [ sp i^ day$ echo$ ] cr
7 times [ i^ cycle order sp sp echo ] cr cr
say "Applying the Friday cycle to a string 10 times:" cr cr
$ "STOREDAILYPUNCH" dup sp sp sp echo$ cr cr
10 times
[ i^ 1+ dup 10 = iff cr else sp
echo sp
friday cycle cypermute dup echo$ cr ]
drop
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: [ [ 2 8 7 3 11 10 15 5 14 4 ] [ 9 12 13 ] ] so that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE or they could use the one-line notation: [ 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10 ] To revert to the Wednesday arrangement they should use these cycles: [ [ 2 4 14 5 15 10 11 3 7 8 ] [ 9 13 12 ] ] or with the one-line notation: [ 1 8 11 2 14 6 3 7 12 15 10 13 9 4 5 ] So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily permutations consecutively, the arrangements will be: RADIOLUNCHTYPES Mon: HANDYCOILSERUPT Tue: SPOILUNDERYACHT Wed: DRAINSTYLEPOUCH Thu: DITCHSYRUPALONE Fri: SOAPYTHIRDUNCLE Sat: SHINEPARTYCLOUD Sun: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: [ [ 1 10 15 7 6 ] [ 2 9 14 13 11 4 8 5 12 ] ] So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: Mon Tue Wed Thu Fri Sat Sun -1 -1 1 -1 -1 1 1 These are the orders of the permutations: Mon Tue Wed Thu Fri Sat Sun 18 30 12 30 10 33 40 Applying the Friday cycle to a string 10 times: STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH
Raku
# one-line
sub infix:<➜> ($start, $end) {$start.fc.comb(/\w/).antipairs.hash{ $end.fc.comb(/\w/) } »+» 1}
# cycle
sub infix:<➰> ($start, $end) {
my @one-line = flat ($end ➜ $start);
my @index = flat 1..+@one-line;
my ($index, @cycles) = 0;
for @index {
my $this = $_ - 1;
while @one-line[$this] {
my $next = @one-line[$this];
@cycles[$index].push: @index[$this];
@one-line[$this] = Nil;
$this = $next - 1;
}
++$index;
}
@cycles.grep: *.elems > 1
}
# order of a cycle
sub order (@cycles) { [lcm] @cycles }
# signature of a cycle
sub signature (@cycles) {
(@cycles.elems %% 2 and all @@cycles».elems %% 2) ?? 1 !! -1
}
# apply a one-line transform
sub apply-o ($string, @oneline) { $string.comb[@oneline].join }
# apply a cyclical transform
sub apply-c ($string, @cycle) {
my @string = flat '', $string.comb;
@cycle.map: { @string[|$_].=rotate(-1) }
@string.join
}
# Alf & Bettys letter arrangements
my %arrangment =
:Mon<HANDYCOILSERUPT>,
:Tue<SPOILUNDERYACHT>,
:Wed<DRAINSTYLEPOUCH>,
:Thu<DITCHSYRUPALONE>,
:Fri<SOAPYTHIRDUNCLE>,
:Sat<SHINEPARTYCLOUD>,
:Sun<RADIOLUNCHTYPES>;
# some convenience variables
my @days = <Sun Mon Tue Wed Thu Fri Sat Sun>;
my @o = @days.rotor(2 => -1).map: { (%arrangment{.[0]} ➜ %arrangment{.[1]}) »-» 1 }
my @c = @days.rotor(2 => -1).map: { (%arrangment{.[0]} ➰ %arrangment{.[1]}) }
my $today;
# The task
say qq:to/ALF&BETTY/;
On Thursdays Alf and Betty should rearrange
their letters using these cycles: {gist %arrangment<Wed> ➰ %arrangment<Thu>}
So that {%arrangment<Wed>} becomes {%arrangment<Wed>.&apply-o: (%arrangment<Wed> ➜ %arrangment<Thu>) »-» 1}
or they could use the one-line notation: {gist %arrangment<Wed> ➜ %arrangment<Thu>}
To revert to the Wednesday arrangement they
should use these cycles: {gist %arrangment<Thu> ➰ %arrangment<Wed>}
or with the one-line notation: {gist %arrangment<Thu> ➜ %arrangment<Wed>}
So that {%arrangment<Thu>} becomes {%arrangment<Thu>.&apply-o: (%arrangment<Thu> ➜ %arrangment<Wed>) »-» 1}
Starting with the Sunday arrangement and applying each of the daily
permutations consecutively, the arrangements will be:
{$today = %arrangment<Sun>}
{join "\n", @days[1..*].map: { sprintf "%s: %s", $_, $today = $today.&apply-o: @o[$++] } }
To go from Wednesday to Friday in a single step they should use these cycles:
{gist %arrangment<Wed> ➰ %arrangment<Fri>}
So that {%arrangment<Wed>} becomes {%arrangment<Fri>}
These are the signatures of the permutations:
Mon Tue Wed Thu Fri Sat Sun
{@c.map(&signature)».fmt("%2d").join: ' '}
These are the orders of the permutations:
Mon Tue Wed Thu Fri Sat Sun
{@c.map(&order)».fmt("%2d").join: ' '}
Applying the Friday cycle to a string 10 times:
{$today = 'STOREDAILYPUNCH'}
{join "\n", (1..10).map: {sprintf "%2d %s", $_, $today = $today.&apply-c: @c[4]} }
ALF&BETTY
say 'and one last transform:';
say 'STOREDAILYPUNCH'.&apply-c: [[<1 6 12 2 3 4 13 15 9 11 5 14 8 10 7>],];
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: ([2 8 7 3 11 10 15 5 14 4] [9 12 13]) So that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE or they could use the one-line notation: (1 4 7 14 15 6 8 2 13 11 3 9 12 5 10) To revert to the Wednesday arrangement they should use these cycles: ([2 4 14 5 15 10 11 3 7 8] [9 13 12]) or with the one-line notation: (1 8 11 2 14 6 3 7 12 15 10 13 9 4 5) So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily permutations consecutively, the arrangements will be: RADIOLUNCHTYPES Mon: HANDYCOILSERUPT Tue: SPOILUNDERYACHT Wed: DRAINSTYLEPOUCH Thu: DITCHSYRUPALONE Fri: SOAPYTHIRDUNCLE Sat: SHINEPARTYCLOUD Sun: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: ([1 10 15 7 6] [2 9 14 13 11 4 8 5 12]) So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: Mon Tue Wed Thu Fri Sat Sun -1 -1 1 1 -1 1 -1 These are the orders of the permutations: Mon Tue Wed Thu Fri Sat Sun 18 30 12 30 10 33 40 Applying the Friday cycle to a string 10 times: STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH and one last transform: AUTOPSYCHILDREN
Wren
I've tried to stick to Alf and Betty's various conventions throughout, particularly when printing anything.
I've also stuck rigidly to the Quackery entry's examples for ease of comparison.
import "./math" for Int
import "./fmt" for Fmt
/*
It's assumed throughout that string arguments are always 15 characters long
and consist of unique upper case letters.
*/
class PC {
// Private method to shift a cycle one place to the left.
static shiftLeft_(cycle) {
var c = cycle.count
var first = cycle[0]
for (i in 1...c) cycle[i-1] = cycle[i]
cycle[-1] = first
}
// Private method to arrange a cycle so the lowest element is first.
static smallestFirst_(cycle) {
var c = cycle.count
var min = cycle[0]
var minIx = 0
for (i in 1...c) {
if (cycle[i] < min) {
min = cycle[i]
minIx = i
}
}
if (minIx == 0) return
for (i in 1..minIx) shiftLeft_(cycle)
}
// Converts a list in one line notation to a space separated string.
static oneLineToString(ol) { ol.join(" ") }
// Converts a list in cycle notation to a string where each cycle is space separated
// and enclosed in parentheses.
static cyclesToString(cycles) {
var cycles2 = []
for (cycle in cycles) cycles2.add("(" + cycle.join(" ") + ")")
return cycles2.toString
}
// Returns a list in one line notation derived from two strings s and t.
static oneLineNotation(s, t) {
var res = List.filled(15, 0)
for (i in 0..14) res[i] = s.indexOf(t[i]) + 1
for (i in 14..0) {
if (res[i] != i + 1) break
res.removeAt(i)
}
return res
}
// Returns a list in cycle notation derived from two strings s and t.
static cycleNotation(s, t) {
var used = List.filled(15, false)
var cycles = []
for (i in 0..14) {
if (used[i]) continue
var cycle = []
used[i] = true
var ix = t.indexOf(s[i])
if (i == ix) continue
cycle.add(i+1)
while (true) {
cycle.add(ix + 1)
used[ix] = true
ix = t.indexOf(s[ix])
if (cycle[0] == ix + 1) {
smallestFirst_(cycle)
cycles.add(cycle)
break
}
}
}
return cycles
}
// Converts a list in one line notation to its inverse.
static oneLineInverse(oneLine) {
var c = oneLine.count
var s = oneLine.map { |b| String.fromByte(b + 64) }.join()
if (c < 15) {
for (i in c..15) s = s + String.fromByte(c + 65)
}
var t = (0..14).map { |b| String.fromByte(b + 65) }.join()
return oneLineNotation(s, t)
}
// Converts a list of cycles to its inverse.
static cycleInverse(cycles) {
var cycles2 = []
for (i in 0...cycles.count) {
var cycle = cycles[i][-1..0]
smallestFirst_(cycle)
cycles2.add(cycle)
}
return cycles2
}
// Permutes a string using perm in one line notation.
static oneLinePermute(s, perm) {
var c = perm.count
var t = List.filled(15, "")
for (i in 0...c) t[i] = s[perm[i]-1]
if (c < 15) {
for (i in c..14) t[i] = s[i]
}
return t.join()
}
// Permutes a string using perm in cycle notation.
static cyclePermute(s, cycles) {
var t = List.filled(15, "")
for (cycle in cycles) {
for (i in 0...cycle.count-1) {
t[cycle[i+1]-1] = s[cycle[i]-1]
}
t[cycle[0]-1] = s[cycle[-1]-1]
}
for (i in 0..14) if (t[i] == "") t[i] = s[i]
return t.join()
}
// Returns a single perm in cycle notation resulting from applying
// cycles1 first and then cycles2.
static cycleCombine(cycles1, cycles2) {
var s = (0..14).map { |b| String.fromByte(b + 65) }.join()
var t = cyclePermute(s, cycles1)
t = cyclePermute(t, cycles2)
return cycleNotation(s, t)
}
// Converts a list in one line notation to cycle notation.
static oneLineToCycle(oneLine) {
var c = oneLine.count
var t = oneLine.map { |b| String.fromByte(b + 64) }.join()
if (c < 15) {
for (i in c..15) t = t + String.fromByte(c + 65)
}
var s = (0..14).map { |b| String.fromByte(b + 65) }.join()
return cycleNotation(s, t)
}
// Converts a list in cycle notation to one line notation.
static cycleToOneLine(cycles) {
var s = (0..14).map { |b| String.fromByte(b + 65) }.join()
var t = cyclePermute(s, cycles)
return oneLineNotation(s, t)
}
// Returns the order of a permutation.
static order(cycles) {
var lens = []
for (cycle in cycles) lens.add(cycle.count)
return Int.lcm(lens)
}
// Returns the signature of a permutation.
static signature(cycles) {
var count = 0
for (cycle in cycles) if (cycle.count % 2 == 0) count = count + 1
return (count % 2 == 0) ? 1 : -1
}
}
var letters = [
"HANDYCOILSERUPT", // Monday
"SPOILUNDERYACHT", // Tuesday
"DRAINSTYLEPOUCH", // Wednesday
"DITCHSYRUPALONE", // Thursday
"SOAPYTHIRDUNCLE", // Friday
"SHINEPARTYCLOUD", // Saturday
"RADIOLUNCHTYPES" // Sunday
]
System.print("On Thursdays Alf and Betty should rearrange their letters using these cycles:")
var cycles = PC.cycleNotation(letters[2], letters[3])
System.print(PC.cyclesToString(cycles))
System.print("So that %(letters[2]) becomes %(PC.cyclePermute(letters[2], cycles))")
System.print("\nOr they could use the one line notation:")
var oneLine = PC.cycleToOneLine(cycles)
System.print(PC.oneLineToString(oneLine))
System.print("\nTo revert to the Wednesday arrangement they should use these cycles:")
var cycles2 = PC.cycleInverse(cycles)
System.print(PC.cyclesToString(cycles2))
System.print("\nOr with the one line notation:")
var oneLine2 = PC.oneLineInverse(oneLine)
System.print(PC.oneLineToString(oneLine2))
System.print("So that %(letters[3]) becomes %(PC.oneLinePermute(letters[3], oneLine2))")
System.print("\nStarting with the Sunday arrangement and applying each of the daily arrangements")
System.print("consecutively, the arrangements will be:")
var days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
System.print("\n %(letters[6])\n")
for (j in 0..6) {
if (j == 6) System.print()
System.write(days[j] + ": ")
var i = (j == 0) ? 6 : j - 1
var ol = PC.oneLineNotation(letters[i], letters[j])
System.print(PC.oneLinePermute(letters[i], ol))
}
System.print("\nTo go from Wednesday to Friday in a single step they should use these cycles:")
var cycles3 = PC.cycleNotation(letters[3], letters[4])
var cycles4 = PC.cycleCombine(cycles, cycles3)
System.print(PC.cyclesToString(cycles4))
System.print("So that %(letters[2]) becomes %(PC.cyclePermute(letters[2], cycles4))")
System.print("\nThese are the signatures of the permutations:")
System.print(days.join(" "))
for (j in 0..6) {
var i = (j == 0) ? 6 : j - 1
var cy = PC.cycleNotation(letters[i], letters[j])
Fmt.write("$2d ", PC.signature(cy))
}
System.print()
System.print("\nThese are the orders of the permutations:")
System.print(days.join(" "))
for (j in 0..6) {
var i = (j == 0) ? 6 : j - 1
var cy = PC.cycleNotation(letters[i], letters[j])
Fmt.write("$3d ", PC.order(cy))
}
System.print()
System.print("\nApplying the Friday cycle to a string 10 times:")
var prev = "STOREDAILYPUNCH"
System.print("\n %(prev)\n")
for (i in 1..10) {
if (i == 10) System.print()
Fmt.write("$2d ", i)
prev = PC.cyclePermute(prev, cycles3)
System.print(prev)
}
- Output:
On Thursdays Alf and Betty should rearrange their letters using these cycles: [(2 8 7 3 11 10 15 5 14 4), (9 12 13)] So that DRAINSTYLEPOUCH becomes DITCHSYRUPALONE Or they could use the one line notation: 1 4 7 14 15 6 8 2 13 11 3 9 12 5 10 To revert to the Wednesday arrangement they should use these cycles: [(2 4 14 5 15 10 11 3 7 8), (9 13 12)] Or with the one line notation: 1 8 11 2 14 6 3 7 12 15 10 13 9 4 5 So that DITCHSYRUPALONE becomes DRAINSTYLEPOUCH Starting with the Sunday arrangement and applying each of the daily arrangements consecutively, the arrangements will be: RADIOLUNCHTYPES Mon: HANDYCOILSERUPT Tue: SPOILUNDERYACHT Wed: DRAINSTYLEPOUCH Thu: DITCHSYRUPALONE Fri: SOAPYTHIRDUNCLE Sat: SHINEPARTYCLOUD Sun: RADIOLUNCHTYPES To go from Wednesday to Friday in a single step they should use these cycles: [(1 10 15 7 6), (2 9 14 13 11 4 8 5 12)] So that DRAINSTYLEPOUCH becomes SOAPYTHIRDUNCLE These are the signatures of the permutations: Mon Tue Wed Thu Fri Sat Sun -1 -1 1 -1 -1 1 1 These are the orders of the permutations: Mon Tue Wed Thu Fri Sat Sun 18 30 12 30 10 33 40 Applying the Friday cycle to a string 10 times: STOREDAILYPUNCH 1 DNPYAOETISLCRUH 2 ORLSEPANTDIUYCH 3 PYIDALERNOTCSUH 4 LSTOEIAYRPNUDCH 5 IDNPATESYLRCOUH 6 TORLENADSIYUPCH 7 NPYIAREODTSCLUH 8 RLSTEYAPONDUICH 9 YIDNASELPROCTUH 10 STOREDAILYPUNCH