Hamming numbers: Difference between revisions

→‎{{header|Elm}}: clean up code...
(→‎{{header|Elm}}: clean up code...)
Line 3,746:
=={{header|Elm}}==
 
The Elm language has many restrictions that make the implementation of the Hamming Number sequence algorithms difficult, as the classic Edsger Dijkstra algorithm as written in Haskell [[Hamming_numbers#The_classic_version]] cannot be written in Elm as current Elm forbids cyclic value references (the value "hamming" is back referenced three times), and the implementation wouldn't be efficient even if it could as the current Elm version 0.19.x has removed the "Lazy" package the would defer the memoization of the result of a computation as necessary in implementing Haskell's lazy lists. Thus, one has to implement memoization using a different data structure than a lazy list; however, all current Elm data structures are persistent/forbid mutation and can only implement some sort of Copy On Write (COW), thus there is no implementation of a linear array and the "Array" module is a tree based structure (with some concessions to data blocks for slightly better performance) that will have a logarithmic execution complexity when the size increases above a minimum. In fact, all Elm data structures that could be used for this also have a logarithmic response (Dict, Set, Array). The implementation of List is not lazy so new elements can't be added to the "tail" but need to be added to the "head" for efficiency, which means if one wants to add higher elements to a list in increasing order, one needs to (COW) reverse the List (twice) in order to do it!
 
The solution here uses a pure functional implementation of a Min Heap (Binary Heap) Priority Queue so that the minimum element can be viewed in O(1) time although inserting new elements/replacing elements still takes O(log n) time where "n" is the number of elements in the queue. As written, no queue needs to be maintained for the multiples of five, but two quesqueues are maintained, one for the merge of the multiples of five and three, and the larger one for the merge of all the multiples of five, three, and two. In order to minimize redundant computation time, the implementation maintains the "next" comparison values as part of the recursive function loop states that can change with every loop.
 
To express the sequence, a Co-Inductive Stream (CIS) is used as a deferred execution (lazy) stream; it does not memoize computations (as discussed above) but that isn't necessary for this application where the sequence is only traversed once and consumed as being traversed.
 
In addition, in order to reduce the "BigInt" computation time, the calculations are done on the basis of a "Float" logarithmic approxitmationapproximation while maintaining "Trival" triple representation of the number of timespowers of two, three, and five, are multiplied in order to obtain the current value represented by the logarithmic approximation. The working code is as follows:
 
<syntaxhighlight lang="elm">module Main exposing ( main )
Line 3,759:
import BigInt
import Task exposing ( Task, succeed, perform, andThen )
import Html exposing (Html, div, text )
import Browser exposing ( element )
import Time exposing ( now, posixToMillis )
Line 3,865:
let omin = case peekMinPQ bpq of
Just (lr, trv) -> LogRep lr trv
otherwiseNothing -> m235 -- at the beginning!
nm235 = multLR2 omin
nbpq = replaceMinPQ m235.lr m235.trv bpq
Line 3,874:
let omin = case peekMinPQ mpq of
Just (lr, trv) -> LogRep lr trv
otherwiseNothing -> mrg -- at the beginning!
nm35 = multLR3 omin
nmrg = if ltLogRep nm35 m5 then nm35 else m5
Line 3,890:
in CIS (0, 0, 0) <| \ () ->
next emptyPQ emptyPQ im235 imrg im35 im5
 
-- following code has to do with outputting to a web page using MUV/TEA...
 
type alias Model = { time: Int
, str1: String, str2: String, str3: String }
 
type Msg = Start Int | Done ( Int, Trival )
 
timemillis : () -> Task Never Int -- a side effect function
Line 3,903 ⟶ 3,896:
test : Int -> Cmd Msg -- side effect function chain (includes "perform")...
test lmt =
let msg1 = "The first 20 Hamming numbers are: " ++
timemillis()
|> andThen (\ t -> succeed ( t, nthCIS lmt (hammingsLog()) ))|> takeCIS2List 20
|> List.map showTrival
, str1: String, str2: |> String.join ", str3:") String++ }"."
msg2 = ("The 1691st Hamming number is " ++
(hammingsLog() |> nthCIS 1691
|> showTrival) ++ ".")
msg3 <|= "The " ++ String.fromInt cLIMIT ++ "th Hamming number is:"
in timemillis()
|> andThen (\ strt ->
let rsltstr = hammingsLog() |> nthCIS (lmt - 1)
|> String.join ", ") ++showTrival ".")in
timemillis()
|> andThen (\ stop ->
succeed [msg1, msg2, msg3, rsltstr ++ " in "
++ String.fromInt (stptmstop - mdl.timestrt) ++
++ " milliseconds." ]))
|> perform Done
 
-- following code has to do with outputting to a web page using MUV/TEA...
myupdate : Msg -> Model -> (Model, Cmd Msg)
 
myupdate msg mdl =
type alias Model = {List time: IntString
case msg of
Start tm -> ( Model tm mdl.str1 mdl.str2 "Running...", test cLIMIT )
Done (stptm, answr) ->
( ( Model stptm mdl.str1 mdl.str2
<| "The " ++ String.fromInt cLIMIT ++
"th Hamming number is " ++
showTrival answr ++ " in " ++
String.fromInt (stptm - mdl.time) ++
" milliseconds." )
, Cmd.none ) -- terminates computation; allows UI update...
 
type Msg = Done Model
myview : Model -> Html msg
myview mdl =
div [] [ div [] [text mdl.str1]
, div [] [text mdl.str2]
, div [] [text mdl.str3] ]
 
main : Program () Model Msg
main = -- starts with empty list of strings; views model of filled list...
main =
element { init = \ _ -> ( Model[], 0test cLIMIT )
, update = \ (Done mdl) _ -> ("The first 20 Hamming numbers are:mdl , "Cmd.none ++)
(hammingsLog() |> takeCIS2List 20
|> List.map showTrival
|> String.join ", ") ++ ".")
("The 1691st Hamming number is " ++
(hammingsLog() |> nthCIS 1691
|> showTrival) ++ ".")
"", perform Start (timemillis()) )
, update = myupdate
, subscriptions = \ _ -> Sub.none
, view = myviewdiv [] << List.map (div [] << List.singleton << text) }</syntaxhighlight>
{{out}}
<pre>The first 20 Hamming numbers are: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 36.
The 1691st Hamming number is 2125764000.
The 1000000th Hamming number is 519312780448388736089589843750000000000000000000000000000000000000000000000000000000 in 756 milliseconds.</pre>:
519298692278785094152510283943756293079298533583627301759029258223616000000000000000 in 756 milliseconds.</pre>
 
Do note that, due to the logarithmic response of the Min Heap Priority Queue, the execution time is logarithmic with number of elements evaluation and not linear as it would otherwise be, so if it takes 0.7 seconds to find the millionth Hamming number, it takes something about 10 seconds to find the ten millionth value instead of about 7 seconds. Considering that the generated "native" code is just JavaScript, it is reasonably fast and somewhat competitive with easier implementations in other languages such as F#.
 
=={{header|Erlang}}==
474

edits