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


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 1p 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.