Cycles of a permutation: Difference between revisions

Content added Content deleted
(Added Wren)
(julia example)
Line 125: Line 125:
##return the signature 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.
#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.

=={{header|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 to either format, and the code is specified so as to duplicate the cycles as written in
the examples given in the task.
<lang julia>""" 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 an identity Perm for permutations of length n """
Perm(n::Int) = Perm(1:n)

""" 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)

""" iterator interface for Perm """
Base.iterate(p::Perm, state = 1) = state > length(p) ? nothing : (p[state], state + 1)

""" index into a Perm """
Base.getindex(p::Perm, i::Int64) = p.a[i]

""" 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 the Perm that functions to change one permutation result to another """
transform(p1::Perm, p2::Perm) = inv(p1) * p2

""" 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])

""" Given an iterable of equal length and a Perm, return a permuted vector of the iterable's elements """
function permuted(iterable, p1::Perm)
p2 = Perm(iterable)
inttoitem = Dict(enumerate(iterable))
return [inttoitem(i) for i in (p1 * p2).data]
end

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()
</lang> {{out}}
<pre>
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
</pre>


=={{header|Quackery}}==
=={{header|Quackery}}==