Количество циклов перестановки

23

Рассмотрим перестановку целых чисел 1, ... n, такую ​​как эта для n = 6:

[5,2,4,3,6,1]

Если вы рассматриваете перестановку как отображение из [1,2,3,4,5,6]в [5,2,4,3,6,1], перестановка может быть разложена на непересекающиеся циклы . Цикл - это подмножество элементов, которые отображаются друг на друга. Например, 1сопоставляется 5, сопоставляется 6, сопоставляется обратно 1. Итак, один цикл есть [1,5,6]. Другие циклы [2]и [3,4]. Таким образом, число циклов для этой перестановки равно 3.

В общем, циклы перестановки являются уникальными (с точностью до порядка), и число циклов для перестановки размера nварьируется от 1до n.

Соревнование

Если дана непустая перестановка, выведите количество циклов.

Входной массив , образованный nцелых чисел 1, 2, ..., n, где n > 0. Каждое целое число встречается ровно один раз. Порядок, в котором они появляются, определяет перестановку, как в примере выше.

Вместо массива вы можете использовать список, строку с разделителем между числами, отдельный ввод для каждого числа или все, что разумно.

Для перестановки размера nвместо набора целых чисел на основе 1 1, ... nможно последовательно использовать набор на основе 0 0, ..., n-1. Если это так, пожалуйста, укажите это в своем ответе.

Код должен работать nдо , чтобы 20в течение разумного времени, скажем , менее чем за одну минуту.

Код гольф. Все встроенные разрешены.

Контрольные примеры

Это предполагает ввод с массива на основе 1.

 [1] -> 1
 [3,2,1] -> 2
 [2,3,4,5,1] -> 1
 [5,2,4,3,6,1] -> 3
 [8,6,4,5,2,1,7,3] -> 2
 [4,5,11,12,7,1,3,9,10,6,8,2] -> 1
 [4,2,5,11,12,7,1,3,9,10,6,8] -> 5
 [5,8,6,18,16,9,14,10,11,12,4,20,15,19,2,17,1,13,7,3] -> 3
 [14,5,17,15,10,18,1,3,4,13,11,16,2,12,9,7,20,6,19,8] -> 7

Связанный

В этой связанной задаче запрашиваются фактические циклы перестановок, а не их количество. Требование только количества циклов может привести к более коротким алгоритмам, которые обходят создание фактических циклов.

Луис Мендо
источник
Не берите в голову мой вопрос, это заявлено в вопросе, основанном на 0 вход разрешен.
orlp
@orlp Это было быстро! Я даже не смог увидеть ваш вопрос
Луис Мендо
Можем ли мы взять отображение индексов на значения в качестве входных данных?
Медь
1
@ Коппер Я думаю, что да, если домен отображения - это множество 1, ..., nв этом порядке. Можете ли вы уточнить, как отображение может быть входом? Это структура данных?
Луис Мендо
@ LuisMendo Да, это структура данных, как Python dict. Я хочу иметь {1: 2, 2: 1}в качестве входа вместо [2, 1].
Медь

Ответы:

12

J, 4 байта

#@C.

Это предполагает, что перестановка основана на 0. Он использует встроенную C.функцию, которая дает список, представляющий прямую перестановку, выводит список циклов. Затем #составляется @на том, что возвращает количество циклов в этом списке.

Попробуй это здесь.

миль
источник
1
Это жульничество! :)
orlp
1
Я должен был запретить buildins :-D
Луис Мендо
2
Встроенные - это любовь. Встроенные - это жизнь. Я согласен, что было бы веселее запретить встроенные функции. Не стесняйтесь изменить правило прямо сейчас, прежде чем слишком много ответов.
миль
@ Майлз Нет, я оставлю все как есть. Отличная работа!
Луис Мендо
7

JavaScript, 99 98 байт

В этом решении предполагается, что массив и его значения имеют нулевую индексацию (например, [2, 1, 0]).

f=a=>{h={},i=c=0;while(i<a.length){s=i;while(!h[i]){h[i]=1;i=a[i]}c++;i=s;while(h[++i]);}return c}

объяснение

// assumes the array is valid and zero-indexed
var findCycles = (array) => {
    var hash = {};  // remembers visited nodes
    var index = 0;  // current node
    var count = 0;  // number of cycles
    var start;      // starting node of cycle

    // loop until all nodes visited
    while(index < array.length) {
        start = index;  // cache starting node

        // loop until found previously visited node
        while(!hash[index]) {
            hash[index] = 1;    // mark node as visited
            index = array[index];   // get next node
        }
        count++;    // increment number of cycles

        index = start + 1;  // assume next node is right after

        // loop until found unvisited node
        while(hash[index]) {
            index++;    // get next node
        }
    }

    return count;   // return number of cycles
};
kamoroso94
источник
3
Добро пожаловать в PPCG! Хороший первый ответ! Это также один из лучших, если не самый лучший, первый ответ, который я видел по своему опыту! Продолжайте хорошую работу!
GamrCorps
Вау, спасибо большое! Я на самом деле должен был посмотреть, как сделать лямбды в JavaScript. Я пока не знаком с ES6.
kamoroso94
6

Mathematica, 45 байт

Length@ConnectedComponents@Thread[Sort@#->#]&

Он генерирует график и считает его связанные компоненты.

alephalpha
источник
6

Mathematica, 37 28 27 байт

#~PermutationCycles~Length&

Спасибо @alephalpha за сохранение 9 байт и @miles за еще 1 байт.

Мартин
источник
3
PermutationCycles[#,Length]&
alephalpha
3
О, это здорово. Я не знал, PermutationCyclesмог бы взять второй аргумент, чтобы изменить заголовок его вывода. Вы также можете использовать инфиксную нотацию для сохранения другого байта #~PermutationCycles~Length&.
миль
1
Также относительно вашего оригинального решения, #&это немного короче, чем Identity. ;)
Мартин Эндер
6

Python, 77 69 67 байт

f=lambda p,i=1:i and0 **p[i-1]+f(p[:i-1]+[0]+p[i:],p[i-1]or max(p))
orlp
источник
(not p[i-1])можно сделать как0**p[i-1]
xnor
5

Желе, 12 10 9 байт

ị³$ÐĿ«/QL

Сохранено 1 байт благодаря @ Dennis .

Это использует перестановки на основе 1. Он работает, применяя перестановку несколько раз, пока не достигнет предыдущей перестановки, сохраняя при этом свои предыдущие значения. Отслеживая изменения, он создаст орбиту для каждого значения вдоль столбцов этой таблицы. Затем, найдя минимум или максимум каждого столбца, можно создать метку для этого цикла. Затем дедуплицируйте этот список меток и получите его длину, которая будет числом непересекающихся циклов.

Попробуй это здесь.

объяснение

ị³$ÐĿ«/QL  Input: permutation p
  $        Chain (ị³) as a monad
 ³           The input p
ị            For each value x, get the value at index x in p
   ÐĿ      Invoke it on p initially, and repeat it on its next value until it returns
           to a previous value and keep track of the results
           This will create a table where each column is the orbit of each value
     «/    Get the minimum value along each column of that table
       Q   Deduplicate
        L  Get the length and return
миль
источник
Очень хороший подход!
Луис Мендо
ị³$ÐĿ«/QLдолжно сработать.
Деннис
@ Деннис Вау, это ловкий трюк! Поскольку каждый цикл не пересекается, достаточно взять максимум / мин и использовать его в качестве метки для дедупликации + длина для результата.
миль
5

Python, 64 байта

l=input()
for _ in l:l=[min(x,l[x])for x in l]
print len(set(l))

Этот гольф-код оказался идиоматичным и читабельным. Использует 0-индексацию.

Каждое значение смотрит на то, на что оно указывает и на что указывает указанное значение, и указывает на меньшее из двух. После достаточного количества повторений каждый элемент указывает на наименьший элемент своего цикла. Число отдельных элементов, на которые указывают, равно количеству циклов.

Достаточно сделать nитерации. В качестве альтернативы, мы можем выполнять итерации, пока список больше не изменится. Эта стратегия дала мне рекурсивную функцию той же длины, 64 байта:

f=lambda l,p=0:len(set(l*(l==p)))or f([min(x,l[x])for x in l],l)

Сокращение составило 65 байт

lambda l:len(set(reduce(lambda l,_:[min(x,l[x])for x in l],l,l)))

Эти set(_)преобразования могут быть сокращены , чтобы {*_}в Python 3.5, сэкономив 2 байта.

XNOR
источник
4

Haskell, 111 байт

l!i|l!!i<0=l|1<2=(take i l++[-1]++drop(i+1)l)!(l!!i)
f(x:y)|x>=0=0|1<2=1+f y
c l|l==[-1|x<-l]=0|1<2=1+c(l!f l)

Использует индексирование на основе 0

Программист
источник
4
Черт, тебе лучше иметь хороший программный шрифт :)1l!i|iIi!!1ll1|
orlp
@orlp и это 111 байтов! : O
grooveplex
4

Pyth, 9 байт

l{mS.u@QN

Использует индексы на основе 0. Попробуйте онлайн .

Как это работает

  m         map for d in input:
    .u        cumulative fixed-point: starting at N=d, repeatedly replace N with
      @QN       input[N]
              until a duplicate is found, and return all intermediate results
   S          sort
 {          deduplicate
l           length
Андерс Касеорг
источник
3

JavaScript (ES6), 49 байт

a=>a.reduce(g=(c,e,i)=>e<i?g(c,a[e],i):c+=e==i,0)

Используется индексирование с нуля. Объяснение: reduceиспользуется для вызова внутренней функции gкаждого элемента массива. cколичество циклов, eэлемент iмассива, индекс массива. Если элемент меньше индекса, то это потенциальный цикл - элемент используется для индексации в массиве для рекурсивного поиска следующего элемента в цикле. Если мы начинаем или заканчиваем с исходного индекса, то это новый цикл, и мы увеличиваем количество циклов. Если в какой-то момент мы найдем значение, превышающее индекс, мы посчитаем этот цикл позже.

Нил
источник
Когда я запустил ваш код в массиве [2,1,0,3,4,5], он вылетел с сообщением «Превышен максимальный размер стека вызовов».
kamoroso94
1
@ kamoroso94 Извините, опечатка закралась. Нужно исправить сейчас.
Нил
2

C, 90 байтов

Вызов f()с изменяемым intмассивом, 1 на основе индексации. Второй параметр - это размер массива. Функция возвращает количество циклов.

i,j,c;f(a,n)int*a;{for(c=i=0;i<n;++i)for(j=0,c+=!!a[i];a[i];a[i]=0,i=j-1)j=a[i];return c;}

Попробуйте это на Ideone .

Алгоритм:

For each index
    If index is non-zero
        Increment counter
        Traverse the cycle, replacing each index in it with 0.
owacoder
источник
2

GAP , 30 байт

Прямо, второй аргумент Cyclesдает множество, на которое должна действовать перестановка:

l->Size(Cycles(PermList(l),l))
Кристиан Сиверс
источник