MathGroup Archive 2000

[Date Index] [Thread Index] [Author Index]

Search the Archive

AW: Re: Re: need little help - no longer!

  • To: mathgroup at smc.vnet.net
  • Subject: [mg23605] AW: [mg23589] Re: [mg23548] Re: [mg23530] need little help - no longer!
  • From: Wolf Hartmut <hwolf at debis.com>
  • Date: Wed, 24 May 2000 02:16:06 -0400 (EDT)
  • Sender: owner-wri-mathgroup at wolfram.com

Dear Andrzej,

a remark has to be made,  see below:

	-----Ursprüngliche Nachricht-----
	Von:	Andrzej Kozlowski [SMTP:andrzej at tuins.ac.jp]
	Gesendet am:	Montag, 22. Mai 2000 00:13
	An:	mathgroup at smc.vnet.net
	Betreff:	[mg23589] Re: [mg23548] Re: [mg23530] need little
help - no longer!

	on 00.5.20 4:10 PM, Wagner Truppel at wtruppel at uci.edu wrote:

	> "Thank you!" to both Johannes Ludsteck and Kenneth Levasseur, who
	> supplied me with solutions to my query.
	> 
	> As Johannes pointed out, the Statistics`MultiDiscreteStatistics`
	> package has facilities for drawing random numbers from a
multinomial
	> probability distribution. However, I personally prefer Ken's
solution:
	> 
	> RandomIndex[m_] :=
	> (m /. {{a__, b___} :> Length[{a}] /; Plus[a] >= #}) &[Random[]]
	> 
	> Once again, thank you both.
	> Wagner
	> 


	I agree that Ken's solution is very elegant and surprisingly a lot
faster
	than the one derived from the MultiDiscreteDistributions package,
but if you
	mainly care about efficiency than it is still pretty inefficient.
The reason
	is that each time the function RandomIndex is run, first a random
number is
	chosen (using uniform distribution) and then repeated pattern
matchings have
	to be made before an index is chosen (this time with multinomial
	distribution). This can be considerably speeded up if one first
makes a list
	of indices containing each with required frequency and then just
chooses one
	of them using Random[] (with uniform distribution). Here is one way
to do
	this.

	As always we have:

	In[1]:=
	m = {0.2, 0.4, 0.1, 0.3}

	Out[1]=
	{0.2, 0.4, 0.1, 0.3}

	This makes a list of indices containing each index with required
frequency:

	indices[m_] := 
	  Module[{l = Map[Rationalize, m]},
	    Flatten[Table @@@
	        Transpose[{Range[Length[m]],
	            Partition[LCM @@ Map[Denominator, l]*l, 1]}]]]

	In this case the list of indices with frequencies becomes:

	In[6]:=
	ind = indices[m]

	Out[6]=
	{1, 1, 2, 2, 2, 2, 3, 4, 4, 4}

	Given such a list we can now define  RandomIndex as

	RandomIndex := ind[[Random[Integer, {1, Length[ind]}]]]

	Now 

	In[7]:=
	N[Count[Table[RandomIndex, {1000}], #] & /@ Range[1, 4]/1000]

	Out[7]=
	{0.201, 0.406, 0.103, 0.309}

	In[8]:=
	Timing[Table[RandomIndex, {1000}]] // First
	Out[8]=
	0.05 Second

	(On a 233 mghz Mac PowerBook).


	This is much faster than all the solutions proposed so far except
Ted
	Ersek's one which, however, in the form he sent it, could not be
applied to
	a case with a large number of possible outcomes. (It can of course
be
	modified to work for arbitrary number of outcomes and then, I think,
it may
	well be the fastest one, but I have not tried doing so and testing
it).

	-- 
	Andrzej Kozlowski
	Toyama International University
	JAPAN

	http://platon.c.u-tokyo.ac.jp/andrzej/
	http://sigma.tuins.ac.jp/


Certainly I agree with you,  the notion of "elegance" is sometimes
misleading.  What _is_ elegance after all?  Considering a piece of code as
"elegant" if it is pleasant, condensed, while still readable, and containing
an aspect of surprise and wit - so I agree of Ken's code as being elegant.
Yet elegance sometimes may draw one off the problem - as for a chess player
when "playing for the gallery".

Having been off for several day's, I tried on this before peeking to the
answers, and came up with 

makeRandomIndex[m_] := 
	  Module[{m2 = Rest[FoldList[Plus, 0, m]], r, quasiSequence =
Sequence}, 
	    With[{code = Which @@ MapThread[
	              quasiSequence[r <= #1, #2] &, {m2,
Range[Length[m2]]}]},
	          (r = Random[]; code) &]]

pick = makeRandomIndex[m]

	(r$152 = Random[]; Which[r$152 <= 0.2, 1, r$152 <= 0.6, 2, r$152 <=
0.7, 3, r$152 <= 1., 4])  &

tt = Table[pick[], {10000}]; // Timing

	{1.592 Second, Null}

N[Count[tt, #] & /@ Range[Length[m]]/10000]

	{0.2108, 0.4012, 0.0904, 0.2976}

The point to be respected is the introduction of that ominous
"quasiSequence".  
Sequence[r <= #1, #2]&  would not do, since that will be interpreted as
Function[r <= #1, #2].  I first had Unevaluated in this place, but this
gives rise to a performance penalty, since Unevaluated remains within the
definition of "pick" and such the internal form of Which has to be (re)built
at runtime for each application.  Of course you may introduce Hold or
another head and then replace that with Sequence after Mapping (and before
executing With), yet I consider this to be less "elegant".

On my machine your code was faster (1.102 Second) for this application,
however when I tried it on another example, e.g.

mt = With[{t = Table[Random[], {10}]}, t/Plus @@ t]

	{0.0808559, 0.103876, 0.102807, 0.0350509, 0.128651, 0.0261343,
0.139011,  0.0463357, 0.170722, 0.166556}

it refused to work.  I isolated two problems:

(1)  instead of  Rationalize one has to apply  Rationalize[#,  dx]&   but

(2) dx ->  Precision[#]  again would not work, since Table rejects that
large iterators and even  dx = 10^-3 gives rise to gorgeous structures.

So your proposal in fact only works for a smaller class of problems
(rationals with small denominators).

Kind regards,  Hartmut



  • Prev by Date: Re: Transforming Mathematica Notebooks into Pdf format with Acrobat Distiller
  • Next by Date: Re: Lost site address
  • Previous by thread: Re: AW: Re: Dirichlet function plot
  • Next by thread: line numbering in packages