Visto il grande successo dell’episodio pilota, prosegue la serie sulla CP all’interno dell’architettura .Net, questa volta utilizzando il linguaggio F♯.
Anche questa volta condividiamo la nostra scelta per l’ambiente di sviluppo: abbiamo deciso di upgradare il nostro sistema a Windows 7 e di usare come IDE Visual Studio™ 2015 Professional Edition.
Poiché per la migliore esperienza è necessario aumentare al massimo il WEI, Windows Experience Index, questa volta l’OS Microsoft runna su una macchina reale (con solo una periferica non riconosciuta) con tanto di Ryzen 5 3600, 32 GB di RAM e AMD Radeon RX 570 8 GB. Questo ci ha permesso di ottenere 7.8/7.9 di WEI, niente male!
Il problema di oggi è il seguente: F - Convolution, una diretta applicazione di NTT.
Breve lista dei vantaggi offerti da F♯ come linguaggio di programmazione:
- Essendo F♯ (Fa diesis maggiore) la tonalità in cui viene intonato il “Va’ pensiero”, è impossibile non essere pervasi da un’ondata di patriottismo scrivendo in questo linguaggio.
- F♯ è una potente combinazione di paradigmi funzionali e OOP: in questo linguaggio è possibile avere sia funzioni di prima classe, sia null reference exceptions.
- Operatori di comparazione chiari come in VB.Net:
=
testa per uguaglianza,<>
per disugugaglianza. L’assegnazione a variabile (possibile solo se questa è stata precedentemente dichiarata conmutable
) si fa invece con<-
. - Parola chiave
rec
per dichiarare le funzioni ricorsive, che possono così essere facilmente distinte dalle funzioni non ricorsive. - Operatori “pipe” (
|>
e<|
) che, intuitivamente, pipano valori tra funzioni. - Presenza di uno unit type meno schizofrenico del
void
di C++. - Supporto per identificatori non ASCII (questo permette di esprimere il proprio patriottismo usando caratteri locali)
- Gli operatori bitwise sono tutti da 3 caratteri:
&&&
,|||
,^^^
,~~~
,>>>
e<<<
). Supponiamo sia perché>>
era già usato dalla composizione di funzioni. - Si accede all’i-esimo elemento di un array con
array.[i]
. Non è chiaro cosa faccia il punto. - Funzione
ignore
per ignorare il valore di ritorno di una funzione evitando che il linter rompa. - Ambiti definiti solo dall’indentazione (come in Python), questo permette di risparmiare moltissimi caratteri.
- Di default l’esecuzione inizia nell’ambito globale, ma questo comportamento può essere modificato aggiungendo a una funzione il tag
[<EntryPoint>]
. Questo compromesso accontenta tutti.
E finalmente ecco il codice:
open System
let P = 998244353L
let R = 646L
let IR = 208611436L
[<EntryPoint>]
let main _ =
let [|N; M|] = Console.ReadLine().Split ' ' |> Seq.map int |> Seq.toArray
let A = Console.ReadLine().Split ' ' |> Seq.map int64 |> Seq.toArray
let B = Console.ReadLine().Split ' ' |> Seq.map int64 |> Seq.toArray
let mutable RPow: int64 array = Array.zeroCreate <| 20
let mutable IRPow: int64 array = Array.zeroCreate <| 20
RPow.[0] <- R
IRPow.[0] <- IR
for j = 1 to 19 do
RPow.[j] <- (RPow.[j - 1] * RPow.[j - 1]) % P
IRPow.[j] <- (IRPow.[j - 1] * IRPow.[j - 1]) % P
let mutable UnitàDItalia: int64 array = Array.zeroCreate <| (1<<<20)
let mutable VivaVerdi: int64 array = Array.zeroCreate <| (1<<<20)
let rec fft (start:int) (size:int) (step:int) (depth: int) =
if size <> 1 then
fft start (size / 2) (step * 2) (depth + 1)
fft (start + step) (size / 2) (step * 2) (depth + 1)
let SpedizioneDeiMille = RPow.[depth]
for j = 0 to size - 1 do
VivaVerdi.[start + j * step] <- UnitàDItalia.[start + j * step]
let mutable ω = 1L
for j = 0 to size / 2 - 1 do
UnitàDItalia.[start + j * step] <- (VivaVerdi.[start + j * step * 2] + ω * VivaVerdi.[start + step + j * step * 2]) % P
UnitàDItalia.[start + (j + size/2) * step] <- (VivaVerdi.[start + j * step * 2] - (ω * VivaVerdi.[start + step + j * step * 2] % P) + P) % P
ω <- (ω * SpedizioneDeiMille) % P
let rec ifft (start:int) (size:int) (step:int) (depth: int) =
if size <> 1 then
ifft start (size / 2) (step * 2) (depth + 1)
ifft (start + step) (size / 2) (step * 2) (depth + 1)
let SpedizioneDeiMille = IRPow.[depth]
for j = 0 to size - 1 do
VivaVerdi.[start + j * step] <- UnitàDItalia.[start + j * step]
let mutable ω = 1L
for j = 0 to size / 2 - 1 do
UnitàDItalia.[start + j * step] <- (VivaVerdi.[start + j * step * 2] + ω * VivaVerdi.[start + step + j * step * 2]) % P
UnitàDItalia.[start + (j + size/2) * step] <- (VivaVerdi.[start + j * step * 2] - (ω * VivaVerdi.[start + step + j * step * 2] % P) + P) % P
ω <- (ω * SpedizioneDeiMille) % P
for i = 0 to A.Length - 1 do
UnitàDItalia.[i] <- A.[i]
fft 0 (1<<<20) 1 0
let mutable VàPensiero = Array.copy UnitàDItalia
for i = 0 to B.Length - 1 do
UnitàDItalia.[i] <- B.[i]
for i = B.Length to UnitàDItalia.Length - 1 do
UnitàDItalia.[i] <- 0L
fft 0 (1<<<20) 1 0
for i = 0 to (1<<<20) - 1 do
UnitàDItalia.[i] <- UnitàDItalia.[i] * VàPensiero.[i]
ifft 0 (1<<<20) 1 0
let sb = new Text.StringBuilder()
for i = 0 to (N + M - 2) do
sb.AppendFormat("{0} ", (UnitàDItalia.[i] * 998243401L % P)) |> ignore
Console.Write(sb.ToString())
0