Up | Next | Prev | PrevTail | Tail |
This package is based on a port of the Maple random polynomial generator together with some support facilities for the generation of random numbers and anonymous procedures.
This package is based on a port of the Maple random polynomial generator together with some support facilities for the generation of random numbers and anonymous procedures.
The operator randpoly
is based on a port of the Maple random polynomial
generator. In fact, although by default it generates a univariate or multivariate
polynomial, in its most general form it generates a sum of products of arbitrary
integer powers of the variables multiplied by arbitrary coefficient expressions, in
which the variable powers and coefficient expressions are the results of calling
user-supplied functions (with no arguments). Moreover, the “variables” can be
arbitrary expressions, which are composed with the underlying polynomial-like
function.
The user interface, code structure and algorithms used are essentially identical to those in
the Maple version. The package also provides an analogue of the Maple rand
random-number-generator generator, primarily for use by randpoly
. There are
principally two reasons for translating these facilities rather than designing comparable
facilites anew: (1) the Maple design seems satisfactory and has already been “proven”
within Maple, so there is no good reason to repeat the design effort; (2) the main use for
these facilities is in testing the performance of other algebraic code, and there is an
advantage in having essentially the same test data generator implemented in both Maple
and REDUCE. Moreover, it is interesting to see the extent to which a facility can be
translated without change between two systems. (This aspect will be described
elsewhere.)
Sections 20.47.2 and 20.47.3 describe respectively basic and more advanced use of
randpoly
; §20.47.4 describes subsidiary functions provided to support advanced use
of randpoly
; §20.47.5 gives examples; an appendix gives some details of the only
non-trivial algorithm, that used to compute random sparse polynomials. Additional
examples of the use of randpoly
are given in the test and demonstration file
randpoly.tst
.
randpoly
The operator randpoly
requires at least one argument corresponding to the
polynomial variable or variables, which must be either a single expression or a list of
expressions.45
In effect, randpoly
replaces each input expression by an internal variable and
then substitutes the input expression for the internal variable in the generated
polynomial (and by default expands the result as usual), although in fact if the input
expression is a REDUCE kernel then it is used directly. The rest of this document
uses the term “variable” to refer to a general input expression or the internal
variable used to represent it, and all references to the polynomial structure, such
as its degree, are with respect to these internal variables. The actual degree
of a generated polynomial might be different from its degree in the internal
variables.
By default, the polynomial generated has degree 5 and contains 6 terms. Therefore, if it is univariate it is dense whereas if it is multivariate it is sparse.
Other arguments can optionally be specified, in any order, after the first compulsory variable argument. All arguments receive full algebraic evaluation, subject to the current switch settings etc. The arguments are processed in the order given, so that if more than one argument relates to the same property then the last one specified takes effect. Optional arguments are either keywords or equations with keywords on the left.
In general, the polynomial is sparse by default, unless the keyword dense is specified as an optional argument. (The keyword sparse is also accepted, but is the default.) The default degree can be changed by specifying an optional argument of the form
degree
= \(\langle \)natural number\(\rangle \) .In the multivariate case this is the total degree, i.e. the sum of the degrees with respect to
the individual variables. The keywords deg and maxdeg can also be used in place of
degree. More complicated monomial degree bounds can be constructed by using
the coefficient function described below to return a monomial or polynomial
coefficient expression. Moreover, randpoly
respects internally the REDUCE
“asymptotic” commands let
, weight
etc. described in §11.4 of the REDUCE
manual, which can be used to exercise additional control over the polynomial
generated.
In the sparse case (only), the default maximum number of terms generated can be changed by specifying an optional argument of the form
terms
= \(\langle \)natural number\(\rangle \) .The actual number of terms generated will be the minimum of the value of terms
and
the number of terms in a dense polynomial of the specified degree, number of variables,
etc.
randpoly
The default order (or minimum or trailing degree) can be changed by specifying an optional argument of the form
ord
= \(\langle \)natural number\(\rangle \) .The keyword is ord
rather than order
because order
is a reserved command name in
REDUCE. The keyword mindeg
can also be used in place of ord
. In the multivariate
case this is the total degree, i.e. the sum of the degrees with respect to the individual
variables. The order normally defaults to 0.
However, the input expressions to randpoly
can also be equations, in which case the
order defaults to 1 rather than 0. Input equations are converted to the difference of their
two sides before being substituted into the generated polynomial. The purpose of this
facility is to easily generate polynomials with a specified zero – for example
Order specification and equation input are extensions of the current Maple version of
randpoly
.
The operator randpoly
accepts two further optional arguments in the form of
equations with the keywords coeffs
and expons
on the left. The right sides of each of
these equations must evaluate to objects that can be applied as functions of no variables.
These functions should be normal algebraic procedures (or something equivalent); the
coeffs
procedure may return any algebraic expression, but the expons
procedure
must return an integer (otherwise randpoly
reports an error). The values returned by
the functions should normally be random, because it is the randomness of the coefficients
and, in the sparse case, of the exponents that makes the constructed polynomial
random.
A convenient special case is to use the function rand
on the right of one or both of
these equations; when called with a single argument rand
returns an anonymous
function of no variables that generates a random integer. The single argument of rand
should normally be an integer range in the form \(a~..~b\), where \(a\), \(b\) are integers such that \(a < b\). The
spaces around (or at least before) the infix operator “..” are necessary in some cases in
REDUCE and generally recommended. For example, the expons
argument might take
the form
n
will be the maximum degree with respect to each variable independently. In the
case of coeffs
the lower limit will often be the negative of the upper limit to give a
balanced coefficient range, so that the coeffs
argument might take the form
The first argument of rand
must be either an integer range in the form \(a~..~b\), where \(a\), \(b\) are
integers such that \(a < b\), or a positive integer \(n\) which is equivalent to the range \(0~..~n-1\). The operator
rand
constructs a function of no arguments that calls the REDUCE random number
generator function random
to return a random integer in the range specified; in the
case that the first argument of rand
is a single positive integer \(n\) the function
constructed just calls random(\(n\))
, otherwise the call of random
is scaled and
shifted.
As an additional convenience, if rand
is called with a second argument that is an
identifier then the call of rand
acts exactly like a procedure definition with the identifier
as the procedure name. The procedure generated can then be called with an empty
argument list by the algebraic processor.
[Note that rand()
with no argument is an error in REDUCE and does not return
directly a random number in a default range as it does in Maple – use instead the
REDUCE function random
(see below).]
The operator proc
provides a generalization of rand
, and is primarily intended to be
used with expressions involving the random
function (see below). Essentially, it
provides a mechanism to prevent functions such as random
being evaluated when the
arguments to randpoly
are evaluated, which is too early. The operator proc
accepts a
single argument which is converted into the body of an anonymous procedure, which is
returned as the value of proc
. (If a named procedure is required then the normal
REDUCE procedure
statement should be used instead.) Examples are given in the
following sections, and in the file randpoly.tst
.
As an additional convenience, this package extends the interface to the standard
REDUCE random
function so that it will directly accept either a natural number or an
integer range as its argument, exactly as for the first argument of rand
. Hence
effectively
rand
is marginally more efficient. However, proc
and the generalized
random
interface allow expressions such as the following anonymous random fraction
generator to be easily constructed:
Rand
is a special case of proc
, and (for either) if the switch comp
is on
(and the
compiler is available) then the generated procedure body is compiled.
Rand
with a single argument and proc
both return as their values anonymous
procedures, which if they are not compiled are Lisp lambda expressions. However, if
compilation is in effect then they return only an identifier that has no external
significance46
but which can be applied as a function in the same way as a lambda expression.
It is primarily intended that such “proc expressions” will be used immediately as input
to randpoly
. The algebraic processor is not intended to handle lambda expressions.
However, they can be output or assigned to variables in algebraic mode, although the
output form looks a little strange and is probably best not displayed. But beware that
lambda expressions cannot be evaluated by the algebraic processor (at least, not without
declaring some internal Lisp functions to be algebraic operators). Therefore, for testing
purposes or curious users, this package provides the operators showproc
and
evalproc
respectively to display and evaluate “proc expressions” output by rand
or
proc
(or in fact any lambda expression), in the case of showproc
provided they are
not compiled.
The file randpoly.tst
gives a set of test and demonstration examples.
The following additional examples were taken from the Maple randpoly
help file and
converted to REDUCE syntax by replacing [ ] by { } and making the other changes
shown explicitly:
randpoly(x); 5 4 3 2 - 54*x - 92*x - 30*x + 73*x - 69*x - 67 randpoly({x, y}, terms = 20); 5 4 4 3 2 3 3 31*x - 17*x *y - 48*x - 15*x *y + 80*x *y + 92*x 2 3 2 2 4 3 + 86*x *y + 2*x *y - 44*x + 83*x*y + 85*x*y 2 5 4 3 + 55*x*y - 27*x*y + 33*x - 98*y + 51*y - 2*y 2 + 70*y - 60*y - 10 randpoly({x, sin(x), cos(x)}); 4 3 3 sin(x)*( - 4*cos(x) - 85*cos(x) *x + 50*sin(x) 2 - 20*sin(x) *x + 76*sin(x)*x + 96*sin(x)) % randpoly(z, expons = rand(-5..5)); % Maple % A generalized random "polynomial"! % Note that spaces are needed around .. in REDUCE. on div; off allfac; randpoly(z, expons = rand(-5 .. 5)); 4 3 -3 -4 -5 - 39*z + 14*z - 77*z - 37*z - 8*z off div; on allfac; % Maple % randpoly([x], coeffs = proc() randpoly(y) end); randpoly({x}, coeffs = proc randpoly(y)); 5 5 5 4 5 3 5 2 5 95*x *y - 53*x *y - 78*x *y + 69*x *y + 58*x *y 5 4 5 4 4 4 3 4 2 - 58*x + 64*x *y + 93*x *y - 21*x *y + 24*x *y 4 4 3 5 3 4 3 3 - 13*x *y - 28*x - 57*x *y - 78*x *y - 44*x *y 3 2 3 3 2 5 2 4 + 37*x *y - 64*x *y - 95*x - 71*x *y - 69*x *y 2 3 2 2 2 2 5 - x *y - 49*x *y + 77*x *y + 48*x + 38*x*y 4 3 2 5 + 93*x*y - 65*x*y - 83*x*y + 25*x*y + 51*x + 35*y 4 3 2 - 18*y - 59*y + 73*y - y + 31 % A more conventional alternative is ... % procedure r; randpoly(y)$ % randpoly({x}, coeffs = r); % or, in fact, equivalently ... % randpoly({x}, coeffs = procedure r; randpoly(y)); randpoly({x, y}, dense); 5 4 4 3 2 3 3 85*x + 43*x *y + 68*x + 87*x *y - 93*x *y - 20*x 2 2 2 2 4 3 - 74*x *y - 29*x *y + 7*x + 10*x*y + 62*x*y 2 5 4 3 - 86*x*y + 15*x*y - 97*x - 53*y + 71*y - 46*y 2 - 28*y + 79*y + 44
The only part of this package that involves any mathematics that is not completely trivial
is the procedure to generate a sparse set of monomials of specified maximum and
minimum total degrees in a specified set of variables. This involves some combinatorics,
and the Maple implementation calls some procedures from the Maple Combinatorial
Functions Package combinat
(of which I have implemented restricted versions in
REDUCE).
Given the maximum possible number \(N\) of terms (in a dense polynomial), the required number of terms (in the sparse polynomial) is selected as a random subset of the natural numbers up to \(N\), where each number indexes a term. In the univariate case these indices are used directly as monomial exponents, but in the multivariate case they are converted to monomial exponent vectors using a lexicographic ordering.
By explicitly enumerating cases with 1, 2, etc. variables, as indicated by the inductive proof below, one deduces that:
Proposition 1. In \(n\) variables, the number of distinct monomials having total degree precisely \(r\) is \(^{r+n-1}C_{n-1}\), and the maximum number of distinct monomials in a polynomial of maximum total degree \(d\) is \(^{d+n}C_n\).
Proof Suppose the first part of the proposition is true, namely that there are at most
The sum follows from the fact that
The proposition holds for 1 variable (\(n = 1\)), because there is clearly 1 distinct monomial of each degree precisely \(r\) and hence at most \(d+1\) distinct monomials in a polynomial of maximum degree \(d\).
Suppose that the proposition holds for \(n\) variables, which are represented by the vector \(X\). Then a homogeneous polynomial of degree \(r\) in the \(n+1\) variables \(X\) together with the single variable \(x\) has the form
The previous proposition is also the basis of the algorithm to map term indices \(m \in \mathbb {N}\) to exponent vectors \(v \in \mathbb {N}^n\), where \(n\) is the number of variables.
Define a norm \(\|\cdot \|\) on exponent vectors by \(\|v\| = \sum _{i=1}^n v_i\), which corresponds to the total degree of the monomial. Then, from the previous proposition, the number of exponent vectors of length \(n\) with norm \(\|v\| \leq d\) is \(N(n,d) = {}^{d+n}C_n\). The elements of the \(m^{\text {th}}\) exponent vector are constructed recursively by applying the algorithm to successive tail vectors, so let a subscript denote the length of the vector to which a symbol refers.
The aim is to compute the vector of length \(n\) with index \(m = m_n\). If this vector has norm \(d_n\) then the index and norm must satisfy
The algorithm therefore has a natural recursive structure that computes the norm of each tail subvector as the recursion stack is built up, but can only compute the first term of each tail subvector as the recursion stack is unwound. Hence, it constructs the exponent vector from right to left, whilst being applied to the elements from left to right. The recursion is terminated by the observation that \(v_1 = d_1 = m_1\) for an exponent vector of length \(n = 1\).
The main sub-procedure, given the required length \(n\) and index \(m_n\) of an exponent vector, must return its norm \(d_n\) and the index of its tail subvector of length \(n-1\). Within this procedure, \(N(n,d)\) can be efficiently computed for values of \(d\) increasing from 0, for which \(N(n,0) = {}^nC_n = 1\), until \(N(n,d) > m\) by using the observation that
Up | Next | Prev | PrevTail | Front |