Joe Riel

9660 Reputation

23 Badges

20 years, 21 days

MaplePrimes Activity


These are replies submitted by Joe Riel

readline returns 0 when there are no more lines to read.  That is the purpose of the while condition.  You are not testing the output of readline after the second call.  Note that building a list this way is not particularly efficient, it is O(n^2), where n is the number of lines read.  A better approach, useful if the file has many lines, is to insert each parsed line into a table, then convert the table to a list.

T := table();
for cnt do
   line := readline("s.txt");
   if line = 0 then break; end if;
   T[cnt] := parse(line);
end do:
L := convert(T, 'list');

A safer approach is to wrap the whole construction in a try statement in which you close the file in a finally clause, that way if an error occurs in the loop, say while parsing, the file is closed.

file := "s.txt";
try 
  T := table();
  for cnt do
    line := readline(file);
    if line = 0 then break; end if;
    T[cnt] := parse(line);
  end do;
finally 
  fclose(file);
end try;
L := convert(T, 'list');

Something to consider if you do go with the table approach is that it will give a different result than member if the list has duplicate entries.  The reason is that member returns the position of the first entry, while the table contains the order of the last entry. They could be made identical by reversing the list passed to the table constructor.

Something to consider if you do go with the table approach is that it will give a different result than member if the list has duplicate entries.  The reason is that member returns the position of the first entry, while the table contains the order of the last entry. They could be made identical by reversing the list passed to the table constructor.

Depending on what the user is doing, another possibility is to not resize the rtable but instead shift components around and keep the effective size of the structure as a parameter.  One can use ArrayTools:-Copy to do this efficiently.  For example

A := Vector[row]([1,2,3,4]):
ArrayTools:-Copy(2,A,2,A,1); # copy indices 3..4 to 2..3, effectively deleting 2.
A;
                [1,3,4,4]


Depending on what the user is doing, another possibility is to not resize the rtable but instead shift components around and keep the effective size of the structure as a parameter.  One can use ArrayTools:-Copy to do this efficiently.  For example

A := Vector[row]([1,2,3,4]):
ArrayTools:-Copy(2,A,2,A,1); # copy indices 3..4 to 2..3, effectively deleting 2.
A;
                [1,3,4,4]


I don't have Maple 9.5 readily at hand, however, the only thing I know that won't work is the short form I used in seq. Try the following.
 

A := <4,5,3,0,8,12>:
n := op(1,A):
elim := [2,3,5]:
keep := subsop(seq(i=NULL, i in elim), [seq(i, i=1..n)]);
                                   keep := [1,4,6]:
A[keep];
                                                                   [ 4]
                                                                   [  ]
                                                                   [ 0]
                                                                   [  ]
                                                                   [12]


I don't have Maple 9.5 readily at hand, however, the only thing I know that won't work is the short form I used in seq. Try the following.
 

A := <4,5,3,0,8,12>:
n := op(1,A):
elim := [2,3,5]:
keep := subsop(seq(i=NULL, i in elim), [seq(i, i=1..n)]);
                                   keep := [1,4,6]:
A[keep];
                                                                   [ 4]
                                                                   [  ]
                                                                   [ 0]
                                                                   [  ]
                                                                   [12]


Nice observation and technique.  However, unless I wasn't looking close enough, the original poster's first call to NLPSolve specifies a domain that includes all of the second call, so the computed maximum should not be smaller, as it is. It appears that the problem is in the setting of Digits wth the fairly large exponents.

Nice observation and technique.  However, unless I wasn't looking close enough, the original poster's first call to NLPSolve specifies a domain that includes all of the second call, so the computed maximum should not be smaller, as it is. It appears that the problem is in the setting of Digits wth the fairly large exponents.

There is an assumption on _Z1:

about(_Z1);
Originally _Z1, renamed _Z1~:
  is assumed to be: integer

It may not be necessary in that the prefix _Z indicates the variable is an integer, however, I don't believe Maple commands look at the form of the name (when operating on expressions).

It can be made substantially faster.  Here is a fairly straightforward version

fouraces2 := proc(n)
local cnt,hand,card;
    cnt := 0;
    to n do
        hand := {};
        do
            card := RandomTools:-MersenneTwister:-GenerateInteger('range' = 1..52);
            if card > 4 then
                break;
            elif card in hand then
                next;
            elif nops(hand) = 3 then
                cnt := cnt+1;
                break;
            else
                hand := hand union {card};
            end if;
        end do;
    end do;
    return cnt;
end proc:

Here is a more sophisticated approach.

rnd := proc() option builtin = RandNumberInterface; end proc:

pick := proc()
local bits;
    bits := rnd(5, 52);
    rnd(6, 52, bits) + 1;
end proc:

fouraces3 := proc(n, picked::Array(datatype=integer[4]))
local cnt,card,i,num;
    cnt := 0;
    num := 4;
    to n do
        if num > 0 then
            for i to 4 do picked[i] := 0; end do;
        end if;
        num := 0;
        do
            card := pick();
            if card > 4 then
                break;
            elif picked[card] = 1 then
                next;
            elif num = 3 then
                cnt := cnt+1;
                break;
            else
                num := num+1;
                picked[card] := 1;
            end if;
        end do;
    end do;
    return cnt;
end proc:

time(fouraces3(10^6,Array(1..4,datatype=integer[4])));
                                 8.812

This can be compiled to reduce the time to about 5 seconds.

 

It can be made substantially faster.  Here is a fairly straightforward version

fouraces2 := proc(n)
local cnt,hand,card;
    cnt := 0;
    to n do
        hand := {};
        do
            card := RandomTools:-MersenneTwister:-GenerateInteger('range' = 1..52);
            if card > 4 then
                break;
            elif card in hand then
                next;
            elif nops(hand) = 3 then
                cnt := cnt+1;
                break;
            else
                hand := hand union {card};
            end if;
        end do;
    end do;
    return cnt;
end proc:

Here is a more sophisticated approach.

rnd := proc() option builtin = RandNumberInterface; end proc:

pick := proc()
local bits;
    bits := rnd(5, 52);
    rnd(6, 52, bits) + 1;
end proc:

fouraces3 := proc(n, picked::Array(datatype=integer[4]))
local cnt,card,i,num;
    cnt := 0;
    num := 4;
    to n do
        if num > 0 then
            for i to 4 do picked[i] := 0; end do;
        end if;
        num := 0;
        do
            card := pick();
            if card > 4 then
                break;
            elif picked[card] = 1 then
                next;
            elif num = 3 then
                cnt := cnt+1;
                break;
            else
                num := num+1;
                picked[card] := 1;
            end if;
        end do;
    end do;
    return cnt;
end proc:

time(fouraces3(10^6,Array(1..4,datatype=integer[4])));
                                 8.812

This can be compiled to reduce the time to about 5 seconds.

 

The reason your method is so slow is that it uses the nasty O(n^2) list building technique, with n = 14^4.  Use a table to save the individual results, then combine the entries into a single list of lists:

X := combinat:-cartprod([[seq(1..14)]$4]):
for cnt while not X[finished] do
    C[cnt] := X[nextvalue]();
end do:
A := [seq(C[cnt], cnt=1..cnt-1)]:

That won't be as efficient as CartProdSeq but it will be usable.

The reason your method is so slow is that it uses the nasty O(n^2) list building technique, with n = 14^4.  Use a table to save the individual results, then combine the entries into a single list of lists:

X := combinat:-cartprod([[seq(1..14)]$4]):
for cnt while not X[finished] do
    C[cnt] := X[nextvalue]();
end do:
A := [seq(C[cnt], cnt=1..cnt-1)]:

That won't be as efficient as CartProdSeq but it will be usable.

Using the Threads package was my first thought, however, I don't understand how it works. Consider the following. which uses a call to the OS sleep command to create a delay:

p := proc()
local self;
    self := Threads:-Self();
    printf("start thread %d\n", self);
    to 5 do
        #printf("thread %d: time = %a\n", self, time['real']());
        print(sprintf("thread %d: time = %a", self, time['real']()));
        system("sleep 2");
    end do;
    printf("finish thread %d\n", self);
end proc:

proc()
local id1,id2;
    id1 := Threads:-Create(p());
    id2 := Threads:-Create(p());
    Threads:-Wait(id1,id2);
    print("finished");
end proc();
start thread 1
start thread 2
                            "thread 1: time = .128"

                           "thread 1: time = 2.150"

                           "thread 1: time = 4.181"

                           "thread 1: time = 6.207"

                           "thread 1: time = 8.239"

finish thread 1
                            "thread 2: time = .150"

                           "thread 2: time = 12.296"

                           "thread 2: time = 14.316"

                           "thread 2: time = 16.337"

                           "thread 2: time = 18.358"

finish thread 2
                                  "finished"

Why are the print outputs not interleaved?  Does print not work with Threads?  If it matters, this is on a single-core machine.  If print is changed to a printf statement (see the commented-out line above), then it works as expected:

start thread 1
thread 1: time = .93e-1
start thread 2
thread 2: time = 2.119
thread 1: time = 2.112
thread 2: time = 4.138
thread 1: time = 6.163
thread 2: time = 8.177
thread 1: time = 10.197
thread 2: time = 12.216
thread 1: time = 14.231
thread 2: time = 16.246
finish thread 1
finish thread 2
                                  "finished"

First 116 117 118 119 120 121 122 Last Page 118 of 195