How to calculate amount of cyclic permuations in an array (variable shift)

Question:

Referring to another question, I would like to print (and count amount of) cyclic permuations of an array. My input to such function would be the array, stride and start value.

My array does not necessarily contain numbers only.

Example: given the array X, 1, 2, 3, Y (5 elements) and a stride of 3, I would have

X, 1, 2     // first line
3, Y, X
1, 2, 3
Y, X, 1
2, 3, Y     // last line since it would be repeating hereafter.

The count would be "5" in this case. In many cases, the count is identical to amount of elements, but not always. With 8 elements and stride=4, it’s 2. Using 8 elements and 6, it is 4.

The array may also contain identical values, such as leadin / leadout and duplicate numbers.

Example: LEADIN, LEADIN, LEADIN, LEADIN, 1, 1, 2, 2, 3, 3, 4, 4, LEADOUT, LEADOUT (for 4 leadin, numbers 1..4 duplicated *2 and 2 leadout. Total element count = 14.

The purpose is to form an endless sequence of subsets, each 1 stride long. There must not be any empty spaces in a subset. All elements must be used, and number must stay the same.

With leadin, trivial example: LI, LI, 1, 2, 3, LO, LO in a stride of 2 will be:

LI LI | 1 2 | 3 LO | LO LI | LI 1 | 2 3 | LO LO (7 repeats).

I would probably be using Python for the job. Getting data from a cyclic array is no problem – but I need to find out how many "shifts" I need to do.

Using this simple function, I can "count" the amount, but I would think there is a formula to do this ?

def getiterations(elements, stride):
    # assuming here that elements > stride
    lc = 0
    lineno = 0
    finished = False
    while not finished:
        lc = lc+stride      # simulate getting N numbers
        lineno= lineno+1
        if (lc %elements)==0:
           finished = True
    return lineno
Asked By: MyICQ

||

Answers:

You just want to calculate the lowest common multiple and then divide by stride length to get the number of cyclic permutations.

import math

num_cycles = math.lcm(elements,stride)/stride
Answered By: Michael Cao

Here is the complete program if it is useful to anyone.

Solution with thanks to @michael-cao.

"""
    Program to create a square array of a fixed-length list, in a sequential cyclic pattern until
    square is filled from first to last.

    Example using [*, * 1,2,3,4, X, X, X]  in a 3-column list
    2 leadin, 3 leadout, 4 values = 8 elements.

      |------------|  stride = 3  (amount per row)
      |----- first element
        *;   *;0001;1
        0002;0003;   X;2
           X;   X;   *;3
           *;0001;0002;4
        0003;   X;   X;5
           X;   *;   *;6
        0001;0002;0003;7
           X;   X;   X;8
                    |---- last element

      The purpose is to print each row on a paper, put these papers in sequence.
      After last row, the first row is printed again. For this reason, the row must be filled.

    To find amount of lines the following formula is used:

              least common multiple (elementcount, stride)
               ---------------------------------------
                        stride

             example:   lcm(8,3) = 24         24 /3  = 8 rows

    to output the list, the itertools module is used
          * cycle
          * islice


"""
import math
from itertools import cycle, islice

# ----------------------------------------------------
# -- configuration
#
leadin = '*'              # which string for leadin
leadincount =  2          # amount of lead in
leadout = 'X'             # which string for leadout
leadoutcount = 3          # amount of leadout
startvalue = 1            # first number in sequence
endvalue = 3              # last number in sequence
startindex  = 0           # zero-based start index for the list..

# ---- options for output
stride=3                  # amount of columns
numberformat='04d'        # formatting number series
textformat  = '>4s'       # formatting string (lead in/out)
listseparator = ';'       # what separates output list...
outputIndex= True         # print index number next to data
#
# ----------------------------------------------------

dataelements = [leadin]*leadincount + list(range(startvalue, endvalue+1)) + [leadout]*leadoutcount


repeats = int(math.lcm(len(dataelements), stride)/stride)

"""
   format the elements according to needs, text or number
"""
def outgroup(d):
    a = []
    for i in d:
        l = str(i)
        if l.isnumeric():
            a.append(f"{i:{numberformat}}")
        else:
            a.append(f"{l:{textformat}}")
    return a
    

for index, *group in zip(
             range(repeats), 
             *[islice(cycle(dataelements), startindex, None)] * stride
             ):
    dataline = listseparator.join(outgroup(group))
    if outputIndex:
        print(f"{dataline}{listseparator}{index+1}")
    else:
        print(f"{dataline}{listseparator}")

Answered By: MyICQ
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.