Erlang "case" clause for values in a range - algorithm

Learning Erlang, I'm solving a simple problem and I came up with this solution:
%%%------------------------------------------------------------------
%%% #doc https://leetcode.com/problems/add-two-numbers/
%%%
%%% #end
%%%------------------------------------------------------------------
-module(add_two_numbers).
-define(BASE10, 10).
-export([main/2]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
-spec main(L1 :: nonempty_list(non_neg_integer()), L2 :: nonempty_list(non_neg_integer())) -> list().
%%%==================================================================
%%% Export
%%%==================================================================
main(L1, L2) ->
loop(L1, L2, 0, []).
-ifdef(TEST).
main_test() ->
?assertEqual([0, 2, 1, 2, 1, 1], add_two_numbers:main([9, 6, 9, 5, 9], [1, 5, 1, 6, 1])),
?assertEqual([3, 6, 9, 7, 5], add_two_numbers:main([4, 1, 8, 7, 2], [9, 4, 1, 0, 3])),
?assertEqual([6, 7, 9, 0, 1], add_two_numbers:main([2, 2, 3, 3], [4, 5, 6, 7])),
?assertEqual([6, 3, 7, 4, 1], add_two_numbers:main([4, 1, 0, 8], [2, 2, 7, 6])),
?assertEqual([2, 7, 9, 1], add_two_numbers:main([2, 7, 1, 0], [0, 0, 8, 1])),
?assertEqual([8, 9, 9, 1], add_two_numbers:main([9, 9, 9], [9, 9, 9])),
?assertEqual([7, 1, 6, 1], add_two_numbers:main([9, 3, 7], [8, 7, 8])),
?assertEqual([3, 5, 6, 1], add_two_numbers:main([6, 6, 6], [7, 8, 9])),
?assertEqual([0, 0, 0], add_two_numbers:main([0, 0, 0], [0, 0, 0])),
?assertEqual([7, 0, 8], add_two_numbers:main([2, 4, 3], [5, 6, 4])),
?assertEqual([0, 2, 2], add_two_numbers:main([0, 1], [0, 1, 2])),
?assertEqual([0, 1, 1], add_two_numbers:main([4, 6], [6, 4])),
?assertEqual([0, 0, 1], add_two_numbers:main([1], [9, 9])),
?assertEqual([0, 1], add_two_numbers:main([], [0, 1])),
?assertEqual([], add_two_numbers:main([], [])),
?assertError(badarith, add_two_numbers:main([0, 1, 2], ["", 5, 6])).
-endif.
%%%==================================================================
%%% Internal
%%%==================================================================
loop([H1 | T1], [H2 | T2], C, R) when H1 + H2 + C >= ?BASE10 ->
loop(T1, T2, 1, R ++ [H1 + H2 + C - ?BASE10]);
loop([], [H | T], C, R) when H + C >= ?BASE10 ->
loop([], T, 1, R ++ [H + C - ?BASE10]);
loop([H | T], [], C, R) when H + C >= ?BASE10 ->
loop([], T, 1, R ++ [H + C - ?BASE10]);
loop([H1 | T1], [H2 | T2], C, R) ->
loop(T1, T2, 0, R ++ [H1 + H2 + C]);
loop([], [H | T], C, R) ->
loop([], T, 0, R ++ [H + C]);
loop([H | T], [], C, R) ->
loop([], T, 1, R ++ [H + C]);
loop([], [], C, R) when C > 0 -> R ++ [C];
loop([], [], _, R) -> R.
What bothers me is how many loop calls I have to define. Eventually there might be better solutions for this.
My question: is there any way I can tell in a case-of if some condition is within a range? Something like this, for instance:
X = H1 + H2 + C,
case X of
X >= 10 -> blah;
_ -> ummm
end.
UPDATE
This is what I was trying to achieve:
loop([H1 | T1], [H2 | T2], C, R) ->
case H1 + H2 + C >= ?BASE10 of
true -> loop(T1, T2, 1, R ++ [H1 + H2 + C - ?BASE10]);
false -> loop(T1, T2, 0, R ++ [H1 + H2 + C])
end;
loop([], [H | T], C, R) ->
case H + C >= ?BASE10 of
true -> loop([], T, 1, R ++ [H + C - ?BASE10]);
false -> loop([], T, 0, R ++ [H + C])
end;
loop([H | T], [], C, R) ->
case H + C >= ?BASE10 of
true -> loop(T, [], 1, R ++ [H + C - ?BASE10]);
false -> loop(T, [], 0, R ++ [H + C])
end;
loop([], [], C, R) ->
case C > 0 of
true -> R ++ [C];
false -> R
end.
...not sure if it's better though.

You could use this little trick
loop([], [], 0, R) -> lists:reverse(R);
loop([], [], 1, R) -> lists:reverse(R, [1]);
loop([], L2, C, R) ->
loop([0], L2, C, R);
loop(L1, [], C, R) ->
loop(L1, [0], C, R);
loop([H1 | T1], [H2 | T2], C, R) ->
case H1 + H2 + C of
S when S >= ?BASE10 ->
loop(T1, T2, 1, [S - ?BASE10 | R]);
S ->
loop(T1, T2, 0, [S | R])
end.
Note I don't use Acc++[X] pattern because it is bad habit make it O(N^2)(See[1]). From performance point of view tail call optimization is not necessary if you have to use lists:reverse/1,2 so this non-tail call version should be as fast as tail call optimised or on some platforms even faster:
main(L1, L2) ->
loop(L1, L2, 0).
loop([], [], 0) -> [];
loop([], [], 1) -> [1];
loop([], L2, C) ->
loop([0], L2, C);
loop(L1, [], C) ->
loop(L1, [0], C);
loop([H1 | T1], [H2 | T2], C) ->
case H1 + H2 + C of
X when X >= ?BASE10 ->
[ X - ?BASE10 | loop(T1, T2, 1) ];
X ->
[ X | loop(T1, T2, 0) ]
end.
Or you can make one step further and remove case and also get + 0 out
loop([], [], 0) -> [];
loop([], [], 1) -> [1];
loop([], [H | T], C) ->
loop_([], T, H + C);
loop([H | T], [], C) ->
loop_([], T, H + C);
loop([H1 | T1], [H2 | T2], C) ->
loop_(T1, T2, H1 + H2 + C).
loop_(L1, L2, S) when S >= ?BASE10 ->
[ S - ?BASE10 | loop(L1, L2, 1) ];
loop_(L1, L2, S) ->
[ S | loop(L1, L2, 0) ].
[1]: Try [L1, L2] = [ [rand:uniform(10)-1 || _ <- lists:seq(1, 100000)] || _ <- [1,2] ], timer:tc(fun() -> add_two_numbers:main(L1, L2) end). with your code. In my code it takes 3.5ms and yours 33s.

You could use 2 helper functions (Not sure it is more efficient, maybe easier to read):
loop([H1 | T1], [H2 | T2], C, R) ->
N = H1 + H2 + C,
loop(T1, T2, carry(N), R ++ sum(N));
loop([], [H | T], C, R) ->
N = H + C,
loop([], T, carry(N), R ++ sum(N));
loop([H | T], [], C, R) ->
N = H + C,
loop(T, [], carry(N), R ++ sum(N));
loop([], [], 0, R) ->
R;
loop([], [], C, R) ->
R ++ [C].
carry(N) when N >= ?BASE10 -> 1;
carry(_) -> 0.
sum(N) when N >= ?BASE10 -> [N - ?BASE10];
sum(N) -> [N].

Related

Prolog: zip 2 lists into one

Suppose, we have 2 lists [1, 2, 3], [3, 2, 1] and we want to create a third list equal to the result of a binary function applied to every corresponding pair of the lists elements.
Something like:
f(x, y) =
-1, if x < y;
0, if x = y;
1 if x > y.
For our example we get [-1, 0, 1].
I created the code:
f(A, B, C) :- ( A < B ->
C = -1
; A =:= B ->
C = 0
; C = 1
).
zip([A], [B], [C]) :- f(A, B, C).
zip([A | T1], [B | T2], [C | T]) :- f(A, B, C), zip(T1, T2, T).
It works:
?- zip([1,2,3], [3,2,1], X).
X = [-1, 0, 1] .
But I have a feeling that I am reinventing the wheel. Is there a system function for this in Prolog?
You probably want to use a maplist predicate
https://www.swi-prolog.org/pldoc/doc_for?object=maplist/4
just like so
?- maplist(f,[1,2,3],[3,2,1],C).
C = [-1, 0, 1].

Constrain two lists to begin with N consecutive elements in total using CLP(FD)

I am trying to constrain two lists to begin with N consecutive elements (E) in total using CLP(FD). Here's an example: let L1 and L2 be two lists of size 6, the elements belong to the domain [0, 1], E = 1, and N = 3, then the predicate constraint(+L1, +L2, +N) would provide the following possible solutions:
L1 = [1, 1, 1, 0, _, _], L2 = [0, _, _, _, _, _];
L1 = [1, 1, 0, _, _, _], L2 = [1, 0, _, _, _, _];
L1 = [1, 0, _, _, _, _], L2 = [1, 1, 0, _, _, _];
L1 = [0, _, _, _, _, _], L2 = [1, 1, 1, 0, _, _].
For lists of size 3, the following would be acceptable:
L1 = [1, 1, 1], L2 = [0, _, _];
L1 = [1, 1, 0], L2 = [1, 0, _];
L1 = [1, 0, _], L2 = [1, 1, 0];
L1 = [0, _, _], L2 = [1, 1, 1].
I eventually want to generalize this to an arbitrary number of lists and domain, but this is the smallest equivalent problem I could think of.
Here's my (failed) attempt so far:
:- use_module(library(clpfd)).
constseq([], _, 0).
constseq([L|_], A, 0) :- L #\= A.
constseq([A|Ls], A, N) :- N1 #= N - 1, constseq(Ls, A, N1).
constraint(L1, L2, N) :-
SL1 + SL2 #= N,
constseq(L1, 1, SL1),
constseq(L2, 1, SL2).
main(L1, L2) :-
length(L1, 6),
length(L2, 6),
constraint(L1, L2, 3).
Although it works, note that I'm generating the solutions through backtracking, and not taking advantage of CLP(FD) to do the dirty job. I'm trying to figure out how to accomplish this, and I'm already looking at predicates like chain/2, and monsters like automaton/3, but something in me tells that there must be a simpler solution to this...
Inspired on SICStus example of exactly/3, here's my latest attempt:
constseq_(_, [], _, 0).
constseq_(X, [Y|L], F, N) :-
X #= Y #/\ F #<=> B,
N #= M+B,
constseq_(X, L, B, M).
constseq(L, E, N) :-
constseq_(E, L, 1, N).
I'd write it as follows (using ECLiPSe with library(ic)):
:- lib(ic).
:- lib(ic_global).
constraint(Xs, Ys, N) :-
Xs #:: 0..1,
Ys #:: 0..1,
sum(Xs) + sum(Ys) #= N,
ordered(>=, Xs),
ordered(>=, Ys).
which gives the desired solutions:
?- length(Xs,6), length(Ys,6), constraint(Xs,Ys,3), labeling(Xs), labeling(Ys).
Xs = [0, 0, 0, 0, 0, 0]
Ys = [1, 1, 1, 0, 0, 0]
Yes (0.00s cpu, solution 1, maybe more) ? ;
Xs = [1, 0, 0, 0, 0, 0]
Ys = [1, 1, 0, 0, 0, 0]
Yes (0.00s cpu, solution 2, maybe more) ? ;
Xs = [1, 1, 0, 0, 0, 0]
Ys = [1, 0, 0, 0, 0, 0]
Yes (0.00s cpu, solution 3, maybe more) ? ;
Xs = [1, 1, 1, 0, 0, 0]
Ys = [0, 0, 0, 0, 0, 0]
Yes (0.00s cpu, solution 4)

How to rewrite a orolog predicate so that it can list all possible answers

The predicate diff(A, B, C), should be true when list C consists of elements from list A, which don't belong to list B.
in_list([], []).
in_list(S, [S | _]).
in_list(S, [H | T]) :-
S \= H,
in_list(S, T).
diff([H], T, [H]) :- not(in_list(H, T)), !.
diff([H | T1], T, [H]) :-
not(in_list(H, T)),
not(diff(T1, T, _)), !.
diff([H | T1], T, [Hr]) :-
diff(T1, T, [Hr]),
H \= Hr,
in_list(H, T), !.
diff([H1 | T2], S2, [H1 | Tr]) :-
not(in_list(H1, S2)),
diff(T2, S2, Tr).
diff([H1 | T2], S2, [Hr | Tr]) :-
in_list(H1, S2),
diff(T2, S2, [Hr | Tr]).
diff([H1 | T2], S2, [Hr | Tr]) :-
in_list(Hr, T2),
Hr \= H1,
not(in_list(Hr, S2)),
diff([H1 | T2], [Hr | S2], Tr).
My problem is that it checks correctly, for example:
?- diff([1, 2, 3, 4], [2], [3, 4, 1]). is true,
but when asked to output all the possible answers I only get:
?- diff([1, 2, 3, 4], [2], R).
R = [1, 3, 4] ;
R = [1, 4, 3] ;
R = [1, 3, 4] ;
false.
Edit: expected output - all of the variations
?- diff([1, 2, 3, 4], [2], R).
R = [1, 3, 4] ;
R = [1, 4, 3] ;
R = [3, 1, 4] ;
R = [3, 4, 1] ;
R = [4, 1, 3] ;
R = [4, 3, 1] ;
false.
Or something along the lines.
Here is what I would do:
my-member([X|_], X).
my-member([_|T], X) :- my-member(T, X).
my-append([], L, L).
my-append([H|T], L, [H|R]) :- my-append(T, L, R).
my-prune([], []).
my-prune([H|T], R) :-
my-prune(T, R1),
(
(my-member(R1, H), my-append([], R1, R));
(not((my-member(R1, H))), my-append([H], R1, R))
), !.
my-complementation-uniques(_, [], []).
my-complementation-uniques([], _, []).
my-complementation-uniques([H|T], R, U) :-
my-complementation-uniques(T, R, U1),
(
(
my-member(R, H),
my-append([], U1, U)
);
(
not((my-member(R, H))),
(
(
my-member(U1, H),
my-append([], U1, U)
);
(
not((my-member(U1, H))),
my-append([H], U1, U)
)
)
)
), !.
my-complementation(L, R, U) :-
my-prune(L, L1),
my-prune(R, R1),
my-complementation-uniques(L1, R1, U).
insert(X, A, Y) :- my-append(L, M, X),
Here is my implementation:
my_diff(L1,L2,R):-not_members(L1,L2,List),combinations(List,R).
not_members([],_,[]).
not_members([H|T],L,R):-member(H,L),not_members(T,L,R).
not_members([H|T],L,[H|R]):- \+member(H,L),not_members(T,L,R).
combinations([],[]).
combinations(L,[X|Xs]) :- choice(X,L,R), combinations(R,Xs).
choice(X,[X|L],L).
choice(X,[H|L],[H|R]) :- choice(X,L,R).
Example:
?- my_diff([1, 2, 3, 4], [2], R).
R = [1, 3, 4] ;
R = [1, 4, 3] ;
R = [3, 1, 4] ;
R = [3, 4, 1] ;
R = [4, 1, 3] ;
R = [4, 3, 1] ;
false.

Evaluate a number in (a few) natural language

Assume a list, each element can be:
a) a number 1,2,...9
b) a number 10, 100, 10000, ... (numbers of the form 10^(2^n) with n>=0).
It is need a (as much simple as possible) rule that evaluates this list to one integer number. Examples of this evaluation are:
[1] => 1
[2] => 2
[10 1] => 11
[2 10 1] => 21
[2 100 1 10 4] => 214
[2 10 1 100 4] => 2104
[2 10 1 100 10000] => 21000000
In other words, numbers 10, 100, ... are the equivalent of tenths, hundreds, million, ... in english and the rule to evaluate is the usual in english and other languages: 10, 100 "multiplies" the values before them, numbers after them are added.
(I know this definition is not an exact one, but finding a good definition is part of the problem. Do not hesitate to requests for more examples if necessary).
Note than, in the same way than in natural language, the number zero is not necessary. Even, like initial languages, is not present in the grammar.
Addendum
The major difficulty in this problem is an expression like [2 10000 3 10] that can not be taken as (2*10000+3)*10, but as 2*10000+3*10. Another example is [2 10 1 10000 3 10] that is (2*10+1)*10000+3*10.
Proof of not homework: Interest on this numbering (and, in general, in natural language) is that, in some context, they are more error-safe than binary. By example, in a context of a supermarket prices, "two thousands blah" keeps some meaning, while 1001blah is totally undefined.
With ingenuity, I would start covering the patterns...
test :- forall(member(L=R, [
[1] = 1,
[2] = 2,
[10, 1] = 11,
[2, 10, 1] = 21,
[2, 100, 1, 10, 4] = 214,
[2, 10, 1, 100, 4] = 2104,
[2, 10, 1, 100, 10000] = 21000000
]), test(L, R)).
test(L, R) :-
pattern(L, E), R =:= E -> writeln(ok(L,R)) ; writeln(ko(L,R)).
pattern([A], A) :- dig(A).
pattern([A, B], A+B) :- ten(A), dig(B).
pattern([A, B, C], A*B+C) :- mul_ten(A, B), dig(C).
pattern([A, B, C, D, E], A*B + C*D + E) :- mul_ten(A,B), mul_ten(C,D), B > D, dig(E).
pattern([A, B, C, D, E], ((A*B+C)*D)+E) :- mul_ten(A,B), ten(D), dig(E). % doubt...
pattern([A, B, C, D, E], (A*B+C)*D*E) :- mul_ten(A,B), ten(D), ten(E). % doubt...
dig(D) :- between(1,9,D).
ten(T) :- between(0,10,E), T =:= 10^(2^E). % 10 -> inappropriate (too much zeroes ?)
mul_ten(M,T) :- between(1,9,M), ten(T). % 9 -> inappropriate ?
plain pattern matching. Running:
?- test.
ok([1],1)
ok([2],2)
ok([10,1],11)
ok([2,10,1],21)
ok([2,100,1,10,4],214)
ok([2,10,1,100,4],2104)
ok([2,10,1,100,10000],21000000)
true.
I think that there is little space for recursion, afaik idioms cover frequently used cases, but without 'smart' evaluation... Anyway, I cannot really find my way in (that is, I would never use) this pattern
[2 10 1 100 4] => 2104
edit now, with DCG and CLP(FD) :
:- use_module(library(clpfd)).
test :- forall(member(L=R, [
[1] = 1,
[2] = 2,
[10, 1] = 11,
[2, 10, 1] = 21,
[2, 100, 1, 10, 4] = 214,
[2, 10, 1, 100, 4] = 2104,
[2, 10, 1, 100, 10000] = 21000000
]), test(L, R)).
test(L, R) :-
phrase(pattern(E), L), R #= E -> writeln(ok(L,R)) ; writeln(ko(L,R)).
pattern(A) --> dig(A).
pattern(A+B) --> ten(A), dig(B).
pattern(A*B+C) --> mul_ten(A, B), dig(C).
pattern(A*B+C*D) --> mul_ten(A, B), mul_ten(C, D).
pattern(A*B + C*D + E) --> mul_ten(A,B), mul_ten(C,D), dig(E).
pattern(((A*B+C)*D)+E) --> mul_ten(A,B), [C], ten(D), dig(E). % doubt...
pattern((A*B+C)*D*E) --> mul_ten(A,B), [C], ten(D), ten(E). % doubt...
dig(D) --> [D], {D #>= 1, D #=< 9}.
ten(T) --> [T], {T #>= 1, T #= (10^(2^E)), E #> 0, E #=< 10}.
mul_ten(M,T) --> dig(M), ten(T).
edit I like the op/3 directive, also...
:- op(100,fx, dig).
:- op(100,fx, ten).
:- op(100,xfx, mul).
pattern(A) --> dig A.
pattern(A+B) --> ten A, dig B.
pattern(A*B+C) --> A mul B, dig(C).
pattern(A*B+C*D) --> A mul B, C mul D.
pattern(A*B+C*D+E) --> A mul B, C mul D, dig E.
pattern(((A*B+C)*D)+E) --> A mul B, [C], ten D, dig E. % doubt...
pattern((A*B+C)*D*E) --> A mul B, [C], ten D, ten E. % doubt...
dig D --> [D], {D #>= 1, D #=< 9}.
ten T --> [T], {T #>= 1, T #= (10^(2^E)), E #> 0, E #=< 10}.
M mul T --> dig M, ten T.

Memory exhaustion while running NDSolve

I run into the "No more memory available" error message in Mathematica. I understand that "Parallelize[]" isn't (obviously) going to help me. Neither has "ClearSystemCache[]".
What gives? Do I just need more RAM?
My Code
Needs["VectorAnalysis`"]
Needs["DifferentialEquations`InterpolatingFunctionAnatomy`"];
Clear[Eq4, EvapThickFilm, h, S, G, E1, K1, D1, VR, M, R]
Eq4[h_, {S_, G_, E1_, K1_, D1_, VR_, M_, R_}] := \!\(
\*SubscriptBox[\(\[PartialD]\), \(t\)]h\) +
Div[-h^3 G Grad[h] +
h^3 S Grad[Laplacian[h]] + (VR E1^2 h^3)/(D1 (h + K1)^3)
Grad[h] + M (h/(1 + h))^2 Grad[h]] + E1/(
h + K1) + (R/6) D[D[(h^2/(1 + h)), x] h^3, x] == 0;
SetCoordinates[Cartesian[x, y, z]];
EvapThickFilm[S_, G_, E1_, K1_, D1_, VR_, M_, R_] :=
Eq4[h[x, y, t], {S, G, E1, K1, D1, VR, M, R}];
TraditionalForm[EvapThickFilm[S, G, E1, K1, D1, VR, M, R]];
L = 318; TMax = 10;
Off[NDSolve::mxsst];
Clear[Kvar];
Kvar[t_] := Piecewise[{{1, t <= 1}, {2, t > 1}}]
(*Ktemp = Array[0.001+0.001#^2&,13]*)
hSol = h /. NDSolve[{
(*S,G,E,K,D,VR,M*)
EvapThickFilm[1, 3, 0.1, 7, 0.01, 0.1, 0, 160],
h[0, y, t] == h[L, y, t],
h[x, 0, t] == h[x, L, t],
(*h[x,y,0] == 1.1+Cos[x] Sin[2y] *)
h[x, y, 0] ==
1 + (-0.25 Cos[2 \[Pi] x/L] - 0.25 Sin[2 \[Pi] x/L]) Cos[
2 \[Pi] y/L]
},
h,
{x, 0, L},
{y, 0, L},
{t, 0, TMax},
MaxStepSize -> 0.1
][[1]]
hGrid = InterpolatingFunctionGrid[hSol];
Error message
No more memory available.
Mathematica kernel has shut down.
Try quitting other applications and then retry.
My OS specs
Intel Core 2 Duo with 4.00 GB ram, 64 bit OS (Windows 7)
Here you may get a taste of what is happening:
Replace
MaxStepSize -> 0.1
by
MaxStepFraction -> 1/30
And run your code.
Then:
p = Join[#,Reverse##]&#
Table[Plot3D[hSol[x, y, i], {x, 0, L}, {y, 0, L},
PlotRange -> {All, All, {0, 4}}],
{i, 7, 8, .1}]
Export["c:\\plot.gif", p]
So, Mma is trying to refine the solution at those peaks, to no avail.

Resources