Обчисліть тотиентну функцію Ейлера


27

Фон

Ейлера totient функція φ(n)визначається як кількість цілих чисел менше або дорівнює , nщо взаємно прості з n, тобто, число можливих значень xв 0 < x <= nпротягом якого gcd(n, x) == 1. У нас було в кілька totient - родинні проблеми і раніше, але ніколи не один , який просто його розрахунку.

Відображення функції тотиента на цілі числа є OEIS A000010 .

Виклик

Давши ціле число n > 0, обчисліть φ(n). Ви можете приймати дані за допомогою аргументів командного рядка, стандартного введення, аргументів функції чи будь-чого іншого розумного. Ви можете давати вихід за допомогою стандартного виводу, повернення значень чи будь-чого іншого розумного. Анонімні функції прийнятні. Ви можете припустити, що вхід не переповнить ваш природний метод зберігання цілих чисел, наприклад, intв C, але ви повинні підтримувати входи до 255. Якщо у вашій мові є вбудована загальна функція, ви можете не використовувати її.

Приклади

φ(1) => 1
φ(2) => 1
φ(3) => 2
φ(8) => 4
φ(9) => 6
φ(26) => 12
φ(44) => 20
φ(105) => 48

Найкоротша відповідь у байтах виграє. Якщо ваша мова використовує кодування, відмінне від UTF-8, згадайте про це у своїй відповіді.


4
Що ж, це було днями. Я не думаю, що повторне звернення має достатню різницю, але якщо б щось було, я закрив би інший, тому що я також не думаю, що повторна заявка нічого не додає. Однак, більша різниця полягає в тому, що одна дозволена вбудована версія, а ця - ні.
— Мартін Ендер

Відмова від вбудованих програм очевидно не впливає на відповіді.
— Джулі Пелтьє,

2
@JuliePelletier Чому це? Моя відповідь Mathematica інакше була б на 19 байт коротше:EulerPhi
— Мартін Ендер

@JuliePelletier GCD дозволено, оскільки обчислення GCD не є заданою проблемою, яку слід вирішити. Звичайно, він може підкреслити кількість байтів на ці відповіді, але це не робить виклик кращим. Я відредагую, щоб уточнити.
— bkul

Відповіді:


13

Математика, 27 22 байт

Range@#~GCD~#~Count~1&

Безіменна функція, яка приймає і повертає ціле число.

Тут не так багато пояснюваного, за винятком того, що @нотація префікса для викликів функцій і ~...~є (ліво-асоціативною) нотацією інфіксації, тому зазначене вище те саме, що:

Count[GCD[Range[#], #], 1] &

11

MATL, 7 байт

t:Zd1=s

Ви можете спробувати TryItOnline . Найпростіша ідея: зробіть вектор від 1 до N і приймайте gcd кожного елемента за допомогою N ( Zdробить gcd). Потім знайдіть, які елементи дорівнюють 1, і підсумуйте вектор, щоб отримати відповідь.


Вбудований _Zpдля тих, хто цікавиться.
— Девід

10

J, 9 байт

(-~:)&.q:

Це засновано на в Jsoftware в статті про totient функцій.

З огляду на n = p 1 e 1 ∙ p 2 e 2 ∙∙∙ p k e k, де p k - простий коефіцієнт n , тотативна функція φ ( n ) = φ ( p 1 e 1 ) φ φ ( p 2 e 2 ) ∙∙∙ φ ( p k e k ) = ( p 1 - 1) p 1 e 1 - 1 ∙ ( p 2 - 1) p 2e 2 - 1 ∙∙∙ ( p k - 1) p k e k - 1 .

Використання

   f =: (-~:)&.q:
   (,.f"0) 1 2 3 8 9 26 44 105
  1  1
  2  1
  3  2
  8  4
  9  6
 26 12
 44 20
105 48
   f 12345
6576

Пояснення

(-~:)&.q:  Input: integer n
       q:  Prime decomposition. Get the prime factors whose product is n
(   )&     Operate on them
  ~:         Nub-sieve. Create a mask where 1 is the first occurrence
             of a unique value and 0 elsewhere
 -           Subtract elementwise between the prime factors and the mask
     &.q:  Perform the inverse of prime decomposition (Product of the values)

Скористайтеся тим, що тотиент є мультиплікативним, щоб зробити інше рішення в J за допомогою рекурсії :)
— Leaky Nun

@LeakyNun Я не думаю, що існує простий спосіб розіграти факторинг, оскільки навіть для використання ітеративної форми [:*/@({.(^-(^<:)){:)2&p:потрібно 24 байти, навіть використовуючи вбудований, щоб отримати праймери та їхні показники. А може, існує коротший шлях, і я цього не бачу.
— милі


7

Haskell, 28 байт

f n=sum[1|1<-gcd n<$>[1..n]]

Використовує узгодження констант Хаскелла . Трюки тут досить стандартні для гольфу, але я поясню широкій аудиторії.

Вираз gcd n<$>[1..n]відображається gcd nна [1..n]. Іншими словами, він обчислює значення gcdз nкожного числа від 1до n:

[gcd n i|i<-[1..n]]

Звідси бажаний вихід - це кількість 1записів, але Haskell не вистачає countфункції. Ідіоматичний спосіб filterутримати тільки 1і взяти отримане length, що є занадто довгим для гольфу.

Натомість, filterімітується ознайомлення зі списком [1|1<-l]із отриманим списком l. Зазвичай розуміння списку пов'язує значення з змінною, як в [x*x|x<-l], але Haskell дозволяє узгодити шаблон, в даному випадку константу 1.

Отже, [1|1<-l]генеруючи 1на кожному матчі 1, ефективно витягуючи лише 1початкові списки. Дзвінок sumна ньому дає його довжину.


Я думаю, що це перша відповідь Хаскелла, яку я насправді розумію. Це така класна мова, але вона настільки відрізняється від більшості інших.
— bkul

Нічого собі, я очікував, що відповідність шаблонів повинна бути вичерпною у списках розуміння. Дякую за хитрість.
— Дамієн

7

Python 2, 44 байти

f=lambda n,d=1:d/n or-f(d)*(n%d<1)-~f(n,d+1)

Менше гольфу:

f=lambda n:n-sum(f(d)for d in range(1,n)if n%d<1)

Використовується формула, згідно з якою Ейлерові коефіцієнти дільників nмають суму n:

введіть тут опис зображення

Значення ϕ(n)може бути рекурсивно обчислено як nмінус суми над нетривіальними дільниками. Ефективно, це робить інверсію Мебіуса на функцію ідентичності. Я використовував той самий метод у гольфі, щоб обчислити функцію Мебіуса .

Завдяки Деннісу за збереження 1 байта з кращим базовим випадком, поширюючи початкове значення +nна +1для кожної з nциклів, виконано як -~.



5

J, 11 байт

+/@(1=+.)i.

Використання

>> f =: +/@(1=+.)i.
>> f 44
<< 20

де >>STDIN і <<STDOUT.

Пояснення

+/ @ ( 1 = +. ) i.
               │
   ┌───────────┴┐
 +/@(1=+.)      i.
   │
 ┌─┼──┐
+/ @ 1=+.
    ┌─┼─┐
    1 = +.

>> (i.) 44            NB. generate range
<< 0 1 2 3 4 ... 43
>> (+.i.) 44          NB. calculate gcd of each with input
<< 44 1 2 1 4 ... 1
>> ((1=+.)i.) 44      NB. then test if each is one (1 if yes, 0 if no)
<< 0 1 0 1 0 ... 1
>> (+/@(1=+.)i.) 44   NB. sum of all the tests
<< 20

Як ви отримали вертикальне представлення дерева? Я думав, що це виробляється лише горизонтально.
— миль

@miles Я сам це набрав.
— Лина монашка

5

Python> = 3,5, 76 64 58 байт

Дякуємо LeakyNun за те, що виграли 12 (!) Байт.

Завдяки Sp3000 за те, що ви граєте в 6 байт.

import math
lambda n:sum(math.gcd(n,x)<2for x in range(n))

Мені подобається читати Python. Це має сенс навіть через гольф.


1
lambda n:sum(gcd(n,x)<2for x in range(n))
— Лина монашка

О, Python нарешті додали gcdдо математичного модуля! Я цього не знав.
— rubik

5

Regex (ECMAScript), 131 байт

Принаймні -12 байт завдяки Deadcode (у чаті)

(?=((xx+)(?=\2+$)|x+)+)(?=((x*?)(?=\1*$)(?=(\4xx+?)(\5*(?!(xx+)\7+$)\5)?$)(?=((x*)(?=\5\9*$)x)(\8*)$)x*(?=(?=\5$)\1|\5\10)x)+)\10|x

Спробуйте в Інтернеті!

Вихід - це довжина відповідності.

Регекси ECMAScript вкрай важко нічого підрахувати. Будь-яка зворотна зміна, визначена поза циклом, буде постійною під час циклу, будь-яка зворотна заздалегідь визначена всередині циклу буде скинута під час циклу. Таким чином, єдиний спосіб перенести стан через ітерації циклу - це використання поточного положення відповідності. Це єдине ціле число, і воно може лише зменшуватися (ну, положення збільшується, але довжина хвоста зменшується, і саме це ми можемо зробити з математики).

Враховуючи ці обмеження, просто підрахувати число копріметів видається неможливим. Натомість ми використовуємо формулу Ейлера для обчислення суті.

Ось як це виглядає в псевдокоді:

N = input
Z = largest prime factor of N
P = 0

do:
   P = smallest number > P that’s a prime factor of N
   N = N - (N / P)
while P != Z

return N

Про це є дві сумнівні речі.

По-перше, ми не економимо дані, а лише поточний продукт, тож як ми можемо дійти до основних факторів введення? Хитрість полягає в тому, що у (N - (N / P)) є такі ж прості коефіцієнти> P, як і N. Це може отримати нові прості коефіцієнти <P, але ми все одно їх ігноруємо. Зауважте, що це працює лише тому, що ми повторюємо найважливіші фактори від найменших до найбільших, інакше не вдасться.

По-друге, ми повинні запам'ятати два числа через ітерації циклу (P і N, Z не рахується, оскільки це постійне), і я просто сказав, що це неможливо! На щастя, ми можемо згорнути ці два числа в одному. Зауважимо, що на початку циклу N завжди буде кратним Z, тоді як P завжди буде меншим за Z. Таким чином, ми можемо просто запам'ятати N + P і витягувати P з модулем.

Ось трохи детальніший псевдо-код:

N = input
Z = largest prime factor of N

do:
   P = N % Z
   N = N - P
   P = smallest number > P that’s a prime factor of N
   N = N - (N / P) + P
while P != Z

return N - Z

А ось коментований вираз:

# \1 = largest prime factor of N
# Computed by repeatedly dividing N by its smallest factor
(?= ( (xx+) (?=\2+$) | x+ )+ )

(?=
        # Main loop!
        (
                # \4 = N % \1, N -= \4
                (x*?) (?=\1*$)

                # \5 = next prime factor of N
                (?= (\4xx+?) (\5* (?!(xx+)\7+$) \5)? $ )

                # \8 = N / \5, \9 = \8 - 1, \10 = N - \8
                (?= ((x*) (?=\5\9*$) x) (\8*) $ )

                x*
                (?=
                        # if \5 = \1, break.
                        (?=\5$) \1
                |
                        # else, N = (\5 - 1) + (N - B)
                        \5\10
                )
                x
        )+
) \10

І як бонус ...

Regex (ECMAScript 2018, кількість збігів), 23 байти

x(?<!^\1*(?=\1*$)(x+x))

Спробуйте в Інтернеті!

Вихід - кількість збігів. ECMAScript 2018 представляє перегляд відстані змінної довжини (оцінюється справа наліво), що дозволяє просто підраховувати всі числа одночасно з введенням.

Виявляється, це незалежно той самий метод, який застосовується в розчині Сітківки Лікі Нуна , і регулярний вираз навіть однакової довжини ( і взаємозамінний ). Я залишаю його тут, тому що це може бути цікавим, що цей метод працює в ECMAScript 2018 (і не тільки .NET).

                        # Implicitly iterate from the input to 0
x                       # Don’t match 0
 (?<!                 ) # Match iff there is no...
                 (x+x)  # integer >= 2...
         (?=\1*$)       # that divides the current number...
     ^\1*               # and also divides the input

4

Perl 6 ,  26 24  22 байт

{[+] (^$^n Xgcd $n) X== 1}
{+grep 2>*,(^$_ Xgcd$_)}
{[+] 2 X>(^$_ Xgcd$_)}

Пояснення:

{
  [+] # reduce using &infix:<+>
    2
    X[>] # crossed compared using &infix:«>»
    (
      ^$_    # up to the input ( excludes input )
      X[gcd] # crossed using &infix:<gcd>
      $_     # the input
    )
}

Приклад:

#! /usr/bin/env perl6
use v6.c;

my &φ = {[+] 2 X>(^$_ Xgcd$_)};

say φ(1) # 1
say φ(2) # 1
say φ(3) # 2
say φ(8) # 4
say φ(9) # 6
say φ(26) # 12
say φ(44) # 20
say φ(105) # 48

say φ 12345 # 6576


4

Джулія, 25 байт

!n=sum(i->gcd(i,n)<2,1:n)

Це просто - sumфункція дозволяє надати йому функцію, яка застосовується перед підсумовуванням, - в основному, еквівалент бігу mapі потім sum. Це безпосередньо підраховує кількість відносно простих чисел менше, ніж n.


4

Python 2, 57 байт

f=lambda n,k=1,m=1:n*(k>n)or f(n-(n%k<m%k)*n/k,k+1,m*k*k)

Перевірте його Ideone .

Фон

За формулою продукту Ейлера ,

Формула продукту Ейлера

де φ позначає тотієнтську функцію Ейлера, а p змінюється лише за простими числами.

Для ідентифікації простих чисел ми використовуємо суперечку теореми Вілсона :

наслідок теореми Вілсона

Як це працює

У всіх випадках змінна m буде дорівнює квадрату факторіалу k - 1 . Насправді ми назвали аргументи за замовчуванням k = 1 і m = 0! 2 = 1 .

Поки k ≤ n , n*(k>n)дорівнює 0 і наступний код orвиконується.

Нагадаємо, m%kвийде 1, якщо m є простим, і 0, якщо ні. Це означає, що x%k<m%kвийде True, якщо і лише тоді, коли обидва k є простим числом, а x ділиться на k .

У цьому випадку (n%k<m%k)*n/kвихід n / k , а віднімання його від n замінює його попереднє значення n (1 - 1 / k) , як у формулі продукту Ейлера. В іншому випадку (n%k<m%k)*n/kвиходить 0 і n залишається незмінною.

Обчисливши вищесказане, збільшуємо k і множимо m на "старе" значення k 2 , зберігаючи, таким чином, бажане співвідношення між k і m , а потім викликаємо f рекурсивно за допомогою оновлених аргументів.

Після того як k перевищує n , n*(k>n)оцінюється на n , яке повертається функцією.


4

Рубін, 32 байти

->n{(1..n).count{|i|i.gcd(n)<2}}

лямбда, яка приймає ціле число n, і повертає підрахунки того, скільки цілих чисел у діапазоні (1..n) є спільними з n.


Привіт, і ласкаво просимо до PPCG! Це чудовий перший пост.
— NoOneIsHere

Ласкаво просимо до головоломки програмування та коду для гольфу! Це чудове перше рішення, так тримати!
— bkul

Дякую, не дуже короткий, мені цікаво, чи можна його вдосконалити.
— Redouane Red

3

Брахілог , 25 байт

:{:1e.$pdL,?$pd:LcCdC}fl.

Пояснення

Брахілог ще не має вбудованого GCD, тому ми перевіряємо, що ці числа не мають спільних простих факторів.

  • Основний предикат:

    :{...}fl.             Find all variables which satisfy predicate 1 when given to it as
                          output and with Input as input.
                          Unify the Output with the length of the resulting list
    
  • Предикат 1:

    :1e.                  Unify Output with a number between Input and 1
        $pdL              L is the list of prime factors of Output with no duplicates
            ,
             ?$pd:LcC     C is the concatenation of the list of prime factors of Input with
                          no duplicates and of L
                     dC   C with duplicates removed is still C
    


3

PowerShell v2 +, 72 байти

param($n)1..$n|%{$a=$_;$b=$n;while($b){$a,$b=$b,($a%$b)};$o+=!($a-1)};$o

PowerShell не має в своєму розпорядженні функцією GCD, тому мені довелося прокати свою власну.

Це займає вхід $n, потім діапазон від 1до $nі передає їх у цикл |%{...}. Кожна ітерація ми встановлюємо два допоміжних змінних , $aі $bпотім виконати НСД whileпетлю. Кожна ітерація ми перевірити , що $bпо - раніше не дорівнює нулю, а потім зберегти $a%$bдо $bі попереднє значення , $bщоб $aдля наступного циклу. Потім ми накопичуємо, чи $aдорівнює рівному 1у нашій вихідній змінній $o. Як тільки цикл for виконаний, ми розміщуємо $oна конвеєрі і вихід неявний.

Як приклад того, як whileпрацює цикл, розглянемо $n=20і ми працюємо $_=8. Перша перевірка є $b=20, тому ми вводимо цикл. Спочатку обчислюємо $a%$bабо 8%20 = 8, що встановлюється $bна той самий час, на який 20встановлюється $a. Перевірте 8=0, і ми вводимо другу ітерацію. Потім ми розраховуємо 20%8 = 4і встановити , що до $b, а потім встановити $aв 8. Перевірте 4=0, і ми вводимо третю ітерацію. Ми розраховуємо 8%4 = 0і встановити , що до $b, а потім встановити $aв 4. Перевіряємо 0=0і ми виходимо з циклу, так що GCD (8,20) є $a = 4. Таким чином, !($a-1) = !(4-1) = !(3) = 0так, $o += 0і ми не рахуємо цього.


3

Фактор, 50 байт

[ dup iota swap '[ _ gcd nip 1 = ] filter length ]

Здійснює діапазон ( iota ) n і вказує n у функцію, яка отримує gcd xn при всіх значеннях 0 <= x <= n , перевіряє, якщо результат дорівнює 1 . Фільтр оригінальний вибір на результат чи GCD хп був 1 , і взяти його довжину .


[ dup iota swap '[ _ gcd nip 1 = ] map sum ]економить 6 байтів (я думаю - не дуже досвідчений з Фактором).
— bkul

@bkul Дякую за пропозицію! : D На жаль, між номерами та t/f(символами) у Факторі немає сумісності , тому єдиний спосіб здійснити це [ dup iota swap '[ _ gcd nip 1 = 1 0 ? ] map sum ], що має таку ж точну довжину, що і поточне рішення.
— кіт

Ах, даг. Знову сильний набір тексту.
— bkul

@bkul Ну, я вдячний за сильної типізації та TYPED:в реальному коді фактора: P
— кіт


2

Сітківка, 36 29 байт

7 байт завдяки Мартіну Ендеру.

.+
$*
(?!(11+)\1*$(?<=^\1+)).

Спробуйте в Інтернеті!

Пояснення

Є два етапи (команди).

Перший етап

.+
$*

Це проста підстановка з регулярними виразами, яка перетворює вхід у таку кількість.

Наприклад, 5буде перетворено на 11111.

Другий етап

(?!(11+)\1*$(?<=^\1+)).

Цей регулярний вимір намагається співставити позиції, які задовольняють умові (спів-прайм з введенням), а потім поверне кількість збігів.


Lookbehind не відступає, хіба що всередині шурху?
— Лина монашка

Шукати навколо не підтримують взагалі.
— Мартін Ендер

Тоді як так, що регекс перевірив кожен дільник?
— Leaky Nun

1
Ну вони роблять відкат до тих пір , поки ви не залишити їх. Поки двигун знаходиться всередині кругообігу, він намагатиметься зробити все можливе, щоб ця відповідна відповідність (або вийшла з ладу у випадку негативного обходу). Але після того, як буде пройдено цикл пошуку, двигун не повернеться в нього, якщо що-небудь після цього вийде з ладу (якщо тільки тоді він також не почне відслідковувати речі перед обхідною ланкою і все одно повинен переоцінювати все).
— Мартін Ендер

2

Лист звичайний, 58 байт

(defun o(x)(loop for i from 1 to x if (=(gcd x i)1)sum 1))

Це проста петля, яка нараховує 1 до заданого n та збільшує суму, якщо gcd = 1. Я використовую ім'я функції o, оскільки t - справжнє булеве значення. Не майже найкоротший, але досить простий.


Чи CL не має якоїсь анонімної функції?
— кіт


2

Python 2 , 44 байти

lambda n:sum(k/n*k%n>n-2for k in range(n*n))

При цьому використовується той же метод , щоб визначити coprimes як моя відповідь на «Coprimes до N» .

Спробуйте в Інтернеті!


1
Я бачу, незначне коригування перевірки на продукт n-1замість того 1, що змушує його працювати n==1.
— Ørjan Johansen


1

Власне, 11 байт

;╗R`╜g`M1@c

Спробуйте в Інтернеті!

Пояснення

;╗R`╜g`M1@c   register stack             remarks

                       44
;                      44 44
 ╗            44       44
  R           44       [1 2 3 .. 44]
       M      44       10                for example
    ╜         44       10 44
     g        44       2
              44       [1 2 1 .. 44]     gcd of each with register
        1     44       [1 2 1 .. 44] 1
         @    44       1 [1 2 1 .. 44]
          c   44       20                count

З вбудованою

▒

Спробуйте в Інтернеті!


Ви також можете використовувати ;╗R`╜g1=`MΣдля того ж підрахунку байтів
— Mego

1

JavaScript (ES6), 67 байт

f=n=>[...Array(n)].reduce(r=>r+=g(n,++i)<2,i=0,g=(a,b)=>b?g(b,a%b):a)


1

APL, 7 байт

+/1=⊢∨⍳

Це поїзд монадичної функції, який займає ціле число праворуч. Підхід тут очевидний: сума ( +/) кількість разів GCD вводу та числа від 1 до входу ( ⊢∨⍳) дорівнює 1 ( 1=).

Спробуйте тут


1

Haskell, 31 30 байт

\n->sum[1|x<-[1..n],gcd n x<2]

1 байт збережено, завдяки @Damien

Вибирає значення з gcd = 1, відображає кожне на 1, а потім бере суму.


Ви можете замінити ==1його<2
— Дамієн

1

Пакетна, 151 145 144 байт

@echo off
set t=
for /l %%i in (1,1,%1)do call:g %1 %%i
echo %t%
exit/b
:g
set/ag=%1%%%2
if not %g%==0 call:g %2 %g%
if %2%==1 set/at+=1

Редагувати: збережено 4 байти, видаливши зайві пробіли. Збережено 1 байт за допомогою +=. Збережено 1 байт, очистивши, tяк +=буде інтерпретувати це як 0би не було. Збережено 1 байт завдяки @ EʀɪᴋᴛʜᴇGᴏʟғᴇʀ.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.