Rotație cu modulo | R-BLOGGERS

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

Cât de bine cunoașteți operatorii dvs. fundamentali în diferite limbi? Exemple „ușoare” ajută la fortificarea acestor cunoștințe, iar compararea limbilor constituie unele descoperiri de detalii de implementare îngrijite.

Am văzut asta de la @gregeganSF pe Mastodon

Postează de @gregegansf@MathStodon.xyz

Vizualizare despre Mastodon

Ceea ce spune

Pentru a roti un număr J-Digit n de k cifre, dacă n ≠ 10^j-1 există o formulă simplă:

Rot (n, j, k) = (n*10^k) mod (10^j-1)

de exemplu, 1234 * 100 MOD 9999 = 3412

De ce? Modul scade (10^J-1) de câte ori din stânga K cifrele de n*10^k, îndepărtându-le din stânga și adăugându-le în dreapta.

Și primul meu gând a fost „Ooh, asta e mișto”, dar al doilea meu gând a fost „O să implementez asta într -o grămadă de limbi!”. Sigur, este un pic de matematică foarte mică de implementat fără margini clare de iterație/recurs, dar asta înseamnă că voi lucra cu unele funcționalități de bază și cred că este foarte important să avem acest lucru blocat în mod confortabil. Să vedem cum merge!

Nu sunt conștient de un „nume” pentru asta ca atare. Scriind -o puțin mai stilată

(rot (n, j, k) = (n times10^k) % (10^j-1) )

Se pare că voi avea nevoie doar de o „putere” și un „modulo”. (j ) există numărul de cifre în (n ) Și sigur, am putea număra asta pe noi înșine și să -l transmitem ca un argument, dar și mai bine ar putea fi să -l calculăm și el. Asta înseamnă să descoperiți câte cifre sunt într -un număr.

Ca întotdeauna, accesul meu este R, așa că hai să începem acolo.

R

În r operatorul de alimentare este ^. Asemenea **dar asta nu este aproape niciodată folosit – există chiar o notă despre asta în documentație

** este tradus în parser în ^dar acest lucru a fost nedocumentat timp de mai mulți ani.

Modulul este %% pe care pot nu Amintiți -vă pentru că este similară cu diviziunea întreg %/%.

Pentru a obține numărul de cifre putem folosi faptul că nchar() mai întâi își va transforma intrarea într -un vector de caracter, deci 12345 devine "12345" și astfel numărul de caractere este numărul de cifre. Dacă nu ar fi fost cazul, aș putea încă să fac

ceiling(log10(314159))
## (1) 6

Dar nchar() Abordarea pare bine. Așezând acele piese într -o funcție care ia numărul și câte locuri pentru a -l muta, obțin

rot <- function(n, k) {
  (n*10^k) %% (10^nchar(n)-1)
}
rot(12345, 3)
## (1) 45123

Puteți vedea că valorile sunt „rotite” ciclic de 3 locuri (la stânga).

R nu are o modalitate încorporată de a realiza acest lucru chiar și pentru un vector de valori („cifrele” rotative ale unui număr întreg este o problemă de jucărie care este puțin probabil să apară efectiv în situații reale). O soluție este pachetul meu {vec} care implementează un tip de efect de buffer pentru vectori

# remotes::install_github("jonocarroll/vec")
library(vec)
v <- as_vec(1:5)
rotate(v, 3)
## (1) 4 5 1 2 3

Sub capotă acest lucru folosește modulo pe indici

x <- 1:5; n <- 3
x((((n - 1 + seq_along(x)) %% length(x))) + 1)
## (1) 4 5 1 2 3

O caracteristică suplimentară este că este nevoie și de valori negative pentru a schimba altfel

rotate(v, -3)
## (1) 3 4 5 1 2

în timp ce rot() Implementarea de mai sus nu poate.

Iulia

Așa cum se întâmplă aproape întotdeauna, funcționalitatea Julia pare mai aproape de ceea ce s -ar putea „aștepta” de la traducerea matematicii sau statisticilor; operatorul de energie rămâne ^dar operatorul modulo este mai familiar %. Există un actual ndigits()
Funcție pentru a obține numărul de cifre care, din câte îmi pot spune de la sursă, nu se convertesc mai întâi la caracter. Exemplele pentru această funcție evidențiază o eșec a abordării R – dacă o valoare este negativă atunci as.character() va produce un număr greșit de cifre. În R:

as.character(-1234)
## (1) "-1234"
nchar(-1234)
## (1) 5

în timp ce se află în Julia

ndigits(-1234)
## 4

Nu avem de -a face cu aspecte negative aici, dar asta este cu siguranță un gotcha.

Implementarea funcției Julia se poate face într -o singură linie, deci nu este nevoie de un
function Cuvânt cheie

rot(n,k) = (n*10^k) % (10^ndigits(n)-1)
## rot (generic function with 1 method)
rot(12345, 3)
## 45123

Dacă noi au fost Lucrând cu vectori, Julia are, de asemenea, o modalitate încorporată de a face rotația ciclică, deși pare să se schimbe în direcția opusă

x = (1, 2, 3, 4, 5)
## 5-element Vector{Int64}:
##  1
##  2
##  3
##  4
##  5
circshift(x, -3)
## 5-element Vector{Int64}:
##  4
##  5
##  1
##  2
##  3
circshift(x, 3)
## 5-element Vector{Int64}:
##  3
##  4
##  5
##  1
##  2

Aici se încheie porțiunea de limbă algol a postului și voi trece la unele limbi în care aceste operațiuni sunt și mai fundamentale …

APL

Când văd operațiuni de matematică „simple” pe numere, acum cred că APL pentru că pentru asta a fost construit inițial și funcționează atât de bine; Fiecare operațiune de matematică pe care o puteți efectua pe o serie de valori are un „glif” care îl reprezintă, așa că ar trebui să fie o „traducere” mai bună a matematicii pe o tablă de black direct la cod.

Un lucru pe care îl vor recunoaște imediat utilizatorii este gliful de atribuire ; Da, acesta este un singur glif, nu R <-dar funcționează la fel ca în R.

Probabil că sunteți familiarizați cu gliful de înmulțire × și adăugare +. Glyph cu putere/exponent este *. Nimic prea surprinzător acolo, sper.

Deoarece - este operația „scădere”, există un glif distinct pentru „negativ”
¯ (o cratimă ridicată) Deci nu este confundată cu scăderea. Modulo ia ceva mai multă inspirație din matematică și este |. Singurele alte glife potențial confuze sunt lungimea și format Care, atunci când este combinat, face ceva foarte asemănător cu „Câte personaje folosește acest lucru?”. Din nou, problema „număr negativ” este aici, dar nu suntem îngrijorați de asta în acest caz.

Reunirea acestor piese necesită cunoașterea faptului că APL evaluează dreptul la stânga, cu argumentul din dreapta unui operator într-o „funcție” fiind notată de Omega () și cel din stânga fiind alfa () Funcția cu care am venit arată așa

    rot←{(¯1+10*≢⍕⍵)|⍵×10*⍺}

Este complet posibil să poată fi scurtat sau îmbunătățit; Am tendința de a trece cu vederea acolo unde parantezele sunt cu adevărat necesare și oportunități de simplificare. Cu toate acestea, dacă citiți din dreapta la stânga, explică calculul pe care îl dorim. Aplicarea acesteia la o anumită valoare înseamnă a -l plasa între cele două argumente ale sale

    3 rot 12345
45123

Încercați -vă singur!

Cea mai mare parte a dificultăților cu care m-am confruntat atunci când am construit acest lucru a avut de-a face cu ordinea operațiunilor care trebuie să fie dreptate la stânga. Există modalități de a „schimba” ordinea argumentelor către o funcție (cum ar fi modulo) pentru a-l face să citească mai mult în mod similar expresiei scrise de mână, dar amândoi nu am putut obține asta pentru a produce răspunsul corect și nu am simțit că este necesar.

În ceea ce privește colaborarea cu vectorii, acolo este APL. Există un glif rotativ care, atunci când este dat, un singur argument inversează un vector, dar cu un al doilea argument face exact ceea ce ne dorim; se rotește de multe locuri

    x←1 2 3 4 5
    3 ⌽ x
4 5 1 2 3

Dacă nu te uiți prea atent la tip Dintre date, putem folosi acest lucru pentru a roti un șir din cifre de caractere folosind din nou format Pentru a face un vector de personaje din număr

    3 ⌽ ⍕12345
45123

(spațiul alb aici este pur și simplu pentru demonstrație; 3⌽⍕12345 funcționează la fel).

UIUA este un limbaj mult mai nou, care are mult sprijin pentru operarea pe date, dar se comportă diferit față de toate cele de mai sus; Este un limbaj bazat pe stivă, astfel încât să lucrați cu date pe o stivă, nu ca variabile. M -am jucat cu ea și mi -a plăcut foarte mult să lucrez cu ea – există chiar și o piesă de exercițiu acum – dar încercând să scriu această soluție, mi -am dat seama că am lucrat doar cu un „lucru” pe stivă, chiar dacă acesta a fost un întreg vector de valori. Această problemă ne invită să lucrăm cu o valoare pentru a fi rotită şi câte locuri pentru a -l roti; Asta însemna că am învățat un lot Despre a descoperi ce valoare din stiva pe care o doresc.

Intrarea operatorilor în UIUA este mult ușurată prin traducere și completare automată; Nu trebuie să vă dați seama cum să tastați Puteți începe să tastați
mod Și atâta timp cât este o finalizare unică, UIUA o va converti în gliful corespunzător. În plus, există câteva trucuri de formatare, cum ar fi luarea unui sufix dublu-undererscore la o funcție pentru a face un glif combinat cu o valoare prestabilită; log__10 se traduce prin ₙ₁₀.

Operatorii de care am nevoie aici sunt modulo înmulțiți ×putere log10 ₙ₁₀tavan și scade -cu scufundarea suplimentară pentru a utiliza a doua valoare pe stivă și înapoi ˜ Pentru a schimba argumentele modulului. Nu m -am putut gândi imediat la o modalitate de a obține curat numărul de cifre ale unei valori (am făcut -o mai târziu, la care mă voi întoarce), așa că am mers log10 ruta și soluția mea este (din nou, dreapta la stânga)

3 12345
˜◿⊙(-1˜ⁿ10⌈ₙ₁₀)⊸טⁿ10
45123

Încercați -vă singur!

Lucrul cu vectori este mult mai curat aici, și există o simplă rotire
asta face la fel ca APL

↻ 3 (1 2 3 4 5)
(4 5 1 2 3)

Citind acest lucru, vectorul este plasat pe stivă, apoi valoarea 3 este pusă în partea de sus a stivei, apoi rotirea ia două valori din stivă și efectuează acea operație, lăsând o valoare (rezultatul) pe stivă.

UIUA are, de asemenea, o caracteristică cu adevărat mișto de „anulare” a unei operații, unde se poate calcula inversul. Dacă aș vrea să transform un șir într -un număr, aș folosi Parse
și pot face opusul prefixându -l cu ONU ° Pentru a face „inegalabil” care transformă un număr într -un șir. Întrucât un șir este doar un vector de caractere (în acest caz), rotirea funcționează în mod natural, deși returnând un șir

↻ 3 °⋕ 12345
"45123"

UIUA merge cu un pas mai departe cu un sub ceea ce necesită o anumită valoare, efectuează o anumită transformare, aplică o funcție, apoi undoes acea transformare. În cazul meu, vreau să „re-parparse”, ceea ce pare o potrivire excelentă pentru acest lucru

⍜°⋕(↻ 3) 12345
45123

și returnează din nou un număr, deoarece în mai jos se aplică transformarea înapoi a analizei.

Cel inegalabil °⋕ este recunoscut ca un compus și este trecut ca primul argument pentru sub, în ​​timp ce am nevoie de rotire și 3 pentru a merge împreună cu parantezele. Dacă aș dori întotdeauna să schimb cu 3 locuri, aș putea folosi dubla subliniere pentru a „atașa” 3 la producția rotativă rotate__3 ↻₃ Dar ceea ce am mai sus permite schimbarea acestui număr.

Privind în acest fel, este mai evident că aș putea obține numărul de cifre cu lengthunparse ca ⧻°⋕dar explorarea modului de a merge pe drumul lung a fost în întregime meritat și nu neapărat mai mult cu ceilinglog__10 ca ⌈ₙ₁₀.


Știam că voi găsi multe lucruri interesante când am văzut asta și am avut dreptate – doar petrecând timpul parcurgând documentația acestor funcții „de bază” mi -a amintit de lucrurile pe care le -am uitat și de unele lucruri noi pe care nu cred că le știam până acum.

Mi -ar plăcea să aud ce cred oamenii despre aceste comparații – există puncte pe care le -am trecut cu vederea? Modalități mai bune de a o face? Funcții diferite în alte limbi? Considerații care mi -au fost dor? Ca întotdeauna, pot fi găsit pe Mastodon și pe secțiunea de comentarii de mai jos.

DevTools :: session_info ()
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value
##  version  R version 4.4.1 (2024-06-14)
##  os       macOS 15.4.1
##  system   aarch64, darwin20
##  ui       X11
##  language (EN)
##  collate  en_US.UTF-8
##  ctype    en_US.UTF-8
##  tz       Australia/Adelaide
##  date     2025-05-03
##  pandoc   3.4 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown)
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package     * version    date (UTC) lib source
##  blogdown      1.19       2024-02-01 (1) CRAN (R 4.4.0)
##  bookdown      0.41       2024-10-16 (1) CRAN (R 4.4.1)
##  bslib         0.8.0      2024-07-29 (1) CRAN (R 4.4.0)
##  cachem        1.1.0      2024-05-16 (1) CRAN (R 4.4.0)
##  cli           3.6.4      2025-02-13 (1) CRAN (R 4.4.1)
##  devtools      2.4.5      2022-10-11 (1) CRAN (R 4.4.0)
##  digest        0.6.37     2024-08-19 (1) CRAN (R 4.4.1)
##  ellipsis      0.3.2      2021-04-29 (1) CRAN (R 4.4.0)
##  evaluate      1.0.3      2025-01-10 (1) CRAN (R 4.4.1)
##  fastmap       1.2.0      2024-05-15 (1) CRAN (R 4.4.0)
##  fs            1.6.5      2024-10-30 (1) CRAN (R 4.4.1)
##  glue          1.8.0      2024-09-30 (1) CRAN (R 4.4.1)
##  htmltools     0.5.8.1    2024-04-04 (1) CRAN (R 4.4.0)
##  htmlwidgets   1.6.4      2023-12-06 (1) CRAN (R 4.4.0)
##  httpuv        1.6.15     2024-03-26 (1) CRAN (R 4.4.0)
##  jquerylib     0.1.4      2021-04-26 (1) CRAN (R 4.4.0)
##  jsonlite      2.0.0      2025-03-27 (1) CRAN (R 4.4.1)
##  JuliaCall     0.17.6     2024-12-07 (1) CRAN (R 4.4.1)
##  knitr         1.48       2024-07-07 (1) CRAN (R 4.4.0)
##  later         1.4.1      2024-11-27 (1) CRAN (R 4.4.1)
##  lifecycle     1.0.4      2023-11-07 (1) CRAN (R 4.4.0)
##  magrittr      2.0.3      2022-03-30 (1) CRAN (R 4.4.0)
##  memoise       2.0.1      2021-11-26 (1) CRAN (R 4.4.0)
##  mime          0.12       2021-09-28 (1) CRAN (R 4.4.0)
##  miniUI        0.1.1.1    2018-05-18 (1) CRAN (R 4.4.0)
##  pkgbuild      1.4.7      2025-03-24 (1) CRAN (R 4.4.1)
##  pkgload       1.4.0      2024-06-28 (1) CRAN (R 4.4.0)
##  profvis       0.4.0      2024-09-20 (1) CRAN (R 4.4.1)
##  promises      1.3.2      2024-11-28 (1) CRAN (R 4.4.1)
##  purrr         1.0.4      2025-02-05 (1) CRAN (R 4.4.1)
##  R6            2.6.1      2025-02-15 (1) CRAN (R 4.4.1)
##  Rcpp          1.0.14     2025-01-12 (1) CRAN (R 4.4.1)
##  remotes       2.5.0      2024-03-17 (1) CRAN (R 4.4.1)
##  rlang         1.1.5      2025-01-17 (1) CRAN (R 4.4.1)
##  rmarkdown     2.28       2024-08-17 (1) CRAN (R 4.4.0)
##  rstudioapi    0.17.1     2024-10-22 (1) CRAN (R 4.4.1)
##  sass          0.4.9      2024-03-15 (1) CRAN (R 4.4.0)
##  sessioninfo   1.2.2      2021-12-06 (1) CRAN (R 4.4.0)
##  shiny         1.9.1      2024-08-01 (1) CRAN (R 4.4.0)
##  urlchecker    1.0.1      2021-11-30 (1) CRAN (R 4.4.0)
##  usethis       3.1.0.9000 2025-03-31 (1) Github (r-lib/usethis@a653d6e)
##  vctrs         0.6.5      2023-12-01 (1) CRAN (R 4.4.0)
##  vec         * 0.1.0      2025-01-07 (1) Github (jonocarroll/vec@595b07e)
##  xfun          0.51       2025-02-19 (1) CRAN (R 4.4.1)
##  xtable        1.8-4      2019-04-21 (1) CRAN (R 4.4.0)
##  yaml          2.3.10     2024-07-26 (1) CRAN (R 4.4.0)
## 
##  (1) /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library
## 
## ──────────────────────────────────────────────────────────────────────────────

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.