challenge n人中m人が当選するくじ

n人の中から公平にm人を選ぶ、くじ引きプログラムを作ってください。

Posted feedbacks - Flatten

Nested Hidden
単純に。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def lot(n, m)
  rest = Array.new(n) {|i| i }
  sel = []
  t = if m <= n/2 then m else n-m end
  n.downto(n-t+1) do |i|
    r = rand(i)
    sel << rest[r]
    rest.delete_at(r)
  end
  (if t == m then sel else rest end).sort
end

こんなんで。
1
2
3
4
5
6
7
8
9
import random
def lot(n,m):
    if n<m:
        print "Error"
        return
    myset = set()
    while len(myset)!=m:
        myset.add(random.randint(0,n-1))
    return myset

適当
1
2
3
4
5
6
public boolean hoge(int n, int m) {
    if ( n < m || n < 0 || m < 0 ) throw new IllegalArgumentException();

    long seed = new java.util.Date().getTime() % n;
    return seed < m;
}

ああ、問題読み間違え。 かならずm人が当たる必要あり。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
	public boolean[] lot(int n, int m) {
	    if ( n < m || n < 0 || m < 0 ) throw new IllegalArgumentException();
	    boolean[] result = new boolean[n];
	    for ( int i = 0; i < n; i++ ) result[i] = i < m;
	    for ( int i = 0; i < n; i++) {
	    	int rnd = (int)(Math.random() * 100) % n;
    		boolean tmp = result[i];
    		result[i] = result[rnd];
    		result[rnd] = tmp;
	    }
	    return result;
	}

lot(1000000,999999)
とかやると止まらない…。

2値でシャッフルは効率悪すぎる…。

Scheme (Gauche) で。素直に。
実行例:
gosh> (pick 100 4)
(12 17 72 45)
gosh> (pick 100 7)
(30 68 74 27 58 41 7)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(use srfi-1)
(use srfi-27)
(random-source-randomize! default-random-source)

(define (pick n m)
  (let loop ((lis (iota n)) (m m) (r '()))
    (cond ((zero? m) r)
          ((null? lis) (error "pool too small"))
          (else (let1 picked (list-ref lis (random-integer (length lis)))
                  (loop (delete picked lis =) (- m 1) (cons picked r)))))))

こんなのどうでしょう?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Array
  def randomize!
    rand_array = sort_by { |i| rand }
    replace rand_array
  end
end

def lot(n, m)
  if n >= m
    (1..n).to_a.randomize!.slice!(1, m).sort
  else
    puts "error: n must be bigger than m!"
    exit
  end
end

C++で書いてみたらCしかなかた...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
vector<int>
shuffle(int n, int m)
{
  vector<int> ret;
  if (n <= 0 || m <= 0 || n < m)
    return ret;

  vector<int> v(n);
  for (int i = 0; i < n; i++)
    v[i] = i + 1;

  int cnt = 0;
  ret.resize(m);
  while (cnt < m) {
    int idx = rand() % v.size();
    int val = v[idx];
    v.erase(v.begin() + idx);
    ret[cnt++] = val;
  }

  sort(ret.begin(), ret.end());
  return ret;
}

上の Ruby コードをもっと短くしてみた。
1
2
3
4
def lot(n, m)
 raise ArgumentError unless m > 0 && n > 0 && m <= n
 (1..n).to_a.sort_by {|i| rand}[0, m]
end

yuさん/elm200さんのやり方の拡張です。

解説。
まずソーティングの代わりに選択アルゴリズムを使うことで
計算量が線形になります。

また、配列の各要素にランダム値を付加し、
その大小に基づいてソートする方法 (sort_by) だと
厳密には公平になりません。

例えば、2人から1人を選ぶとき
各々に割り振ったランダム値 (r[x] とします) 
r[0] < r[1] となる確率と r[0] > r[1] となる確率は等確率となりますが
わずかに r[0] == r[1] となることがあり、
その場合、例えば先頭からm個とると必ず先の人が選ばれることになるので、
前の人が若干有利になってしまいます。
これは、比較関数自体をランダムにすることで解決します。(多分)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <vector>
#include <algorithm>
#include <cstdlib>
using namespace std;

bool randcmp(const int n1, const int n2)
{ return rand() &1; }

vector<int> lot(int n, int m)
{
  vector<int> v(n);
  for (int i = 0; i < n; ++i) { v[i] = i+1; }
  const int t = (m <= n/2) ? m : n-m;
  vector<int>::iterator first = v.begin(), mid = v.begin()+t;
  nth_element(first, mid-1, v.end(), randcmp);
  if (t < m) { first = mid; mid = v.end(); }
  return vector<int>(first, mid);
}

common lispで作ってあります。:-)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
;
; choose-winners for common lisp version 0.2 by ytakenaka
;
; 使い方: (choose-winners 候補者リスト 選ぶ人の数) => 当選者リスト
;     (choose-winners '(john taro mika mick) 2)
;            example: =>  (john taro)  リスト内で選ばれたもの
;
; (エラー処理)
; 候補者リスト: ドット付きリスト や リスト以外のもの => nil
; 選ぶ人の数:  候補者リストの長さより大きい => nil
; 選ぶ人の数:  負 => nil

(defun choose-winners (candidates-list number)
  (labels ((choose-1 (candit-lst len)
             ; 候補者リストから一人の当選者と当選者を除いた残りのリストを多値で返す関数
             ; candit-lst 候補者リスト len 候補者リストの大きさ
             (let* ((select-num (random len))
                    (select-el (nth select-num candit-lst)))
               (values select-el (remove select-el candit-lst :count 1))))

           (choose-n (lst winners-lst num len)
             ; 候補者リストからnum人の当選者を選ぶ関数
             ; lst 候補者リスト winners-lst 当選者リスト
             ; num 残り当選者数 len 候補者リストの大きさ
             (if (zerop num)
                 ; 残り当選者数が0になったときに当選者リストを返す。
                 winners-lst
                 (multiple-value-bind (winner rest)
                     (choose-1 lst len)
                   ; 当選者を当選者リストにくわえて、残りの候補者リストを再帰させる。
                   (choose-n rest (cons winner winners-lst)
                             (1- num) (1- len)))))) 

    (and (consp candidates-list)
         (null (cdr (last candidates-list)))
         (let ((length (length candidates-list)))
           (when (and (>= length number) (< 0 number))
                (choose-n candidates-list nil number length))))))

common lispです。
(lot 10 5)
 => (4 10 8 9 1)
(lot 10 5)
 => (3 10 7 8 9)
(lot 10 5)
 => (5 6 9 2 3)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(defun lot (n m)
  "lot 対象人数 当選者数 => 当選者番号のリスト"
  (labels ((range (x acc)    ;レンジを作成する関数
                  (if (zerop x) acc 
                    (range (1- x) (cons x acc))))
           (draw (lst acc)   ;くじを引く関数
                 (if (<= m (length acc))
                   acc
                   (let* ((winner 
                            (nth (random (length lst) (make-random-state t)) lst))
                          (loosers
                            (remove-if #'(lambda (x) (eql winner x)) lst)))
                     (draw loosers (cons winner acc))))))
    (draw (range n nil) nil)))

こんな感じで使ってください。 alert(['Aさん', 'Bさん', 'Cさん'].lot(2));
1
2
3
4
5
6
Array.prototype.lot = function(n) {
  for(var i=0,r=[];i<n;i++)
    with(Math) with(this)
      r.push(splice(floor(random()*length), 1)[0]);
    return r;
};

PHPで。
$m > $n の時、全員が選ばれます
1
2
3
4
5
6
<?php
function lot($n, $m) {
   $a = range(1, $n);
   shuffle($a);
   return array_slice($a, -$m);
}

これでどうだ!
1
2
3
4
5
6
7
8
sub lot {
    my ($n, $m) = @_;
    my @list = map { $_->[0] } 
               sort { $a->[1] <=> $b->[1] } 
               map { [$_, rand] } 
                 (1 .. $n);
    splice(@list, 0, $m);
}

つい書いてしまった。
1
2
3
4
5
sub lot {
    my ($n, $m) = @_;
    my @a = 0..$n-1;
    map { splice @a, int rand scalar @a, 1 } 1..$m;
}

てか m 人が常に連続でも題意は満たしてるよね。ということで1ステートメントに。
1
2
3
4
sub lot {
    my ($n, $m) = @_;
    splice @{[ 0..$n-1, 0..$n-1 ]}, int rand $n, $m;
}

Pythonのrandomモジュールには、与えられたリストをシャッフルするshuffleという関数があります。
1
2
3
4
5
def lot(n, m):
    from random import shuffle
    people = range(n)
    shuffle(people)
    return people[:m]

その発想はなかったです。
確かに当選者を連続にとっても、それぞれの人の当選確率は同じなので公平ですね。
その発想をPythonで実装しました。
1
2
3
4
def lot(n, m):
    from random import random
    start = int(random() * n)
    return (range(n)+range(m))[start:start + m]

PostgreSQL以外はよく知りませんが。
1
2
3
4
5
6
7
=# create function lot(int, int) returns setof int as $$
  select * from generate_series(1, $1) order by random() limit $2;
$$ language sql;
=# select * from lot(10, 3);

普通は関数作るまでもなく、
=# select * from generate_series(1, 10) order by random() limit 3;

O(m)
n < m のとき循環します。
1
2
3
def lot(n,m)
  ((r=rand(n))...(r+m)).map {|e| e%n + 1 }
end

Pythonのrandomは高機能です。
1
2
3
4
def lot(n,m):
    from random import sample
    people = xrange(n)
    return sample(people,m)


	
1
2
3
4
| n m |
n := 10.
m := 5.
^((1 to: n) asArray shuffled allButFirst: m) sort

すみません。違ってました(^_^;)。

ח allButFirst:

○ first:

実際に使用する時は範囲チェック(1<=m<=n)を追加すること。
1
(1..n).sort_by{rand}[0,m]

Arrow を使えばもっと綺麗に書けるかも。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import System.Random

shuffle :: [a] -> StdGen -> ([a], StdGen)
shuffle [] g = ([], g)
shuffle xs g = let (needle  , g'          ) = randomR (0, length xs - 1) g
                   (left    , picked:right) = splitAt needle xs
                   (unpicked, g''         ) = shuffle (left ++ right) g'
               in
                 (picked:unpicked, g'')

lot :: Int -> Int -> StdGen -> ([Int], StdGen)
lot n m g = let (shuffled, g') = shuffle [1..n] g
            in
              (take m shuffled, g')

main = do xs <- getStdRandom (lot 1000000 999999)
          mapM_ (putStrLn . show) xs

echo John Paul George Ringo | awk -v m=2 -f lot.awk のようにして使います。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function swap(a, b,  tmp) { tmp = $a; $a = $b; $b = tmp }

{
  n = NF
  while (m) {
    swap(int(rand() * n + 1), n)
    print $n
    n--; m--;
  }
}

我ながら強引すぎるな... (libcは動くかどうかわかりません汗)「:echo Lot(10, 5)」で実行
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fun! Lot(n, m)
  let v = range(1, a:n)
  while len(v) > a:m
    if has('win32')
      let r = libcallnr("msvcrt", "rand", 0)
    else
      let r = libcallnr("libc", "rand", 0)
    endif
    silent! call remove(v, (r % 10))
  endwhile
  return v
endfun

あ、ハードコーディング...orz
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fun! Lot(n, m)
  let v = range(1, a:n)
  while len(v) > a:m
    if has('win32')
      let r = libcallnr("msvcrt", "rand", 0)
    else
      let r = libcallnr("libc", "rand", 0)
    endif
    silent! call remove(v, (r % len(v)))
  endwhile
  return v
endfun

srand(time(NULL)); sel1(M, N); って感じで
1
2
3
4
5
6
7
8
void sel1(unsigned long m, unsigned long n)
{
  unsigned long i, orgN=n;
  for(i=1; i<=orgN; ++i){
    if((double)rand()/(RAND_MAX+1) < (double)m/n){ printf("%lu\n", i);  --m; }
    --n;
  }
}

一応O(nm)になってるかな?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/local/bin/perl

use strict;
use warnings;

use Data::Dumper;

my $nmax = shift;
my $mmax = shift;

my @winner;

#warn "selecting $nmax from $mmax\n";
for my $n ( 0 .. $nmax - 1 ) {
        my $count = 0;
        for my $m ( 1 .. $mmax ) {
                next if grep { defined $_ and $_ == $m } @winner;
                if ( rand( $count ) < 1 ) {
                        $winner[$n] = $m;
                }
                $count++;
        }
}

print join( "\n", @winner) , "\n";

進捗表示と範囲チェックを追加。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/local/bin/perl

use strict;
use warnings;

use Data::Dumper;

my $nmax = shift;
my $mmax = shift;

if ( $nmax >= $mmax ) {
        print "$_\n" for ( 1 .. $mmax );
        exit;
}

my @winner;

#warn "selecting $nmax from $mmax\n";
for my $n ( 0 .. $nmax - 1 ) {
        print STDERR '.';
        my $count = 0;
        for my $m ( 1 .. $mmax ) {
                next if grep { defined $_ and $_ == $m } @winner;
                if ( rand( $count ) < 1 ) {
                        $winner[$n] = $m;
                }
                $count++;
        }
}

print STDERR "\n";
print join( "\n", @winner) , "\n";

CL-USER> (lot 9999 4)
(6921 3598 7669 9882)
CL-USER> (lot 9999 4)
(46 7796 5867 7725)
CL-USER> (lot 9999 4)
(6973 5502 1591 5060)
CL-USER> (lot 9999 4)
(23 5583 9408 419)
CL-USER> (lot 9999 4)
(1785 6633 7933 8614)
CL-USER> (lot 9999 4)
(3871 7900 7919 1041)
1
2
3
4
5
6
(defun lot (n m)
  (let ((lot (loop for i from 1 to n collect i)))
    (loop repeat m collect
         (let ((x (nth (random (length lot)) lot)))
           (setf lot (delete x lot))
           x))))

jってこれでよかった(一様にランダムになる)か?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public static bool[] MakeDrawing(int n, int m)
{
    if (n < 0) throw new ArgumentException();
    if (m < 0 || m > n) throw new ArgumentException();
    bool[] result = new bool[n];
    Random r = new Random();
    for (int i = 0; i < n; i++)
    {
        int j = r.Next(i + 1);
        result[i] = result[j];
        result[j] = (i < m);
    }
    return result;
}

(lot m n)でm人中n人分の当選番号(1~m)をリストで返します。。
(lot m n lst)でm人中n人分、lstから当選者を抽出したリストを返します。
lstがmより長かったり短かったりしたらnilを返します:p
m<nの時は全員当選します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(defun lot (n m &optional (lst (let ((i 0))
                                 (mapcar #'(lambda (x) (+ (incf i) x))
                                         (make-list n :initial-element 0)))))
  (and
   (= (length lst) n)
   (labels
       ((pickup (lst m)
          (labels
              ((pickup1 (lst n m acc)
                 (if (or (zerop n) (zerop m))
                     acc
                   (let ((r (random n)))
                     (if (zerop r)
                         (pickup1 (cdr lst)
                                  (1- n)
                                  (1- m)
                                  (cons (car lst) acc))
                       (pickup1 (nconc (subseq lst 0 r)
                                       (subseq lst (1+ r)))
                                (1- n)
                                (1- m)
                                (cons (nth r lst) acc)))))))
            (pickup1 lst n m nil))))
     (pickup lst m))))

今ひとつエレガントじゃないなぁ。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# -*- coding: utf-8 -*-
import random

def getRandList(x):
    l = range(x)
    random.shuffle(l)
    return l

def chusen(n,m):
    return getRandList(n)[:m]

print chusen(10,5)
print chusen(50,10)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
open System;;
open List;;

let rec take n ls =
    match n, ls with
    | n, _ when n <= 0 -> []
    | _, [] -> []
    | n, (hd::tl) -> hd :: take (n-1) tl;;

let shuffle ls =
    let rnd = new Random() in
    sort (fun x y-> rnd.Next(-1,2)) ls;;

let choice m n = take m (shuffle (init n (fun x -> x)));;

C++ならstd::random_shuffle使えばいいね。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Collections.Generic;
class Program
{
  static void Main()
  {
    int n = 10, m = 3, seed = 1;
    Random r = new Random(seed);
    foreach (int i in GetRandom(n, m, r))
      Console.WriteLine(i);
  }
  public static int[] GetRandom(int n, int m, Random r)
  {
    n = Math.Max(n, 0);
    m = Math.Min(m, n);
    List<int> lst = new List<int>(n);
    for (int i = 0; i < n; ++i) lst.Add(i);
    for (int i = 0; i < m; ++i)
    {
      int k = r.Next(n - i) + i;
      int t = lst[i];
      lst[i] = lst[k];
      lst[k] = t;
    }
    lst.RemoveRange(m, n - m);
    lst.Sort();
    return lst.ToArray();
  }
}

なんと。お題そのままの関数があったとは…。

とりあえず一発でやるならこうかなあ。
1
2
3
4
5
import scala.util.Sorting
def lot(n:Int, m:Int):Seq[Int] = {
  Sorting.stableSort[Int, Double](List.range(0,n),{x=>Math.random}).take(m)
}
println(lot(2,10).mkString(","))

n <- 10
m <- 5
sample(n, m)
[1]  3  1  9  6 10
1
sample(n, m)

選ばれた人を消去して重複回避。不細工だなぁ。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.List;
import java.util.ArrayList;
import java.util.Random;

public class LotGame {

    public List<Integer> lot(int n, int m) {
        List<Integer> result = new ArrayList<Integer>();
        List<Integer> base = range(n);
        Random random = new Random();
        for (int i = 0; i < m; i++) {
            int j = random.nextInt(base.size() - 1);
            result.add(base.get(j));
            base.remove(j);
        }
        return result;
    }

    private List<Integer> range(int n) {
        List<Integer> result = new ArrayList<Integer>();
        for (int i = 0; i < n; i++) {
            result.add(i);
        }
        return result;
    }

    public static void main(String[] args) {
        for (int i : new LotGame().lot(10, 5)) {
            System.out.println(i);
        }
    }
}

1
2
3
4
5
6
(defun lot (n m)
  (let ((lot (loop for i from 1 to n collect i)))
    (loop repeat m collect
         (let ((x (nth (random (length lot)) lot)))
           (setf lot (delete x lot))
           x))))

Mathematica6で追加された関数を使用.
1
choice[n_, m_] := RandomSample[Range[n], m];

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function lot(n, m)
  math.randomseed(os.time())
  lot = {}
  for i = 1, n do
    table.insert(lot,i)
  end

  ret = {}
  for i = 1, m do
    pos = math.random(table.getn(lot))
    x = lot[ pos ]
    table.remove(lot, pos)
    table.insert(ret,x)
  end
  return ret
end

function print_lot(n, m)
  table.foreach(lot(n, m), function(k,v) print(v) end)
end

print_lot(9999, 4)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
module doukaku;
private import std.stdio;
private import std.random;

void remove(int[] ary, int pos) {
    if (ary.length != pos+1)
        for (int i=pos+1; i < ary.length; i++)
            ary[i-1] = ary[i];
    ary.length = ary.length - 1;
}

int[] lot(int n, int m) {
    int i;
    int[] lot, ret;
    for (i=1; i<=n; i++) lot ~= i;

    for (i=1; i<=m; i++) {
        int pos = rand() % lot.length;
        int x = lot[pos];
        remove(lot, pos);
        lot.length = lot.length - 1;
        ret ~= x;
    }
    return ret;
}

void print_lot(int n, int m) {
    foreach(x; lot(n, m)) writefln(x);
}

void main() {
    print_lot(9999, 4);
}

やっていることはMathematica版と同じですね。

出力例:
>> lot(100, 10)
ans =
    80    42    71    97    51     4    81    94     2    92
1
2
3
4
function x = lot(n, m)
  A = randperm(n);
  x = A(1:m);
end

訂正。結局はランダム・サンプリングになるわけですが、やっていることが違うので、Mathematica版と同値ではないですね。もうしわけないです。 自分の回答では1〜nの整数をランダムに並べ替え、先頭からm個を取り出しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{ Free Pascal + Run-Time Library (rtl)
  2 args
  kuji n m
}
program kuji;
uses
  SysUtils;
Var
  n, m : Cardinal; 

begin
  if ParamCount <> 2 then Exit;
  n := StrToInt(paramstr(1));
  m := StrToInt(paramstr(2));
  if n < m then Exit; 

  Writeln('# n=', IntToStr(n), ' m=', IntToStr(m));
  Randomize;

  for n := n downto 1 do
  begin
    if Random(n)+1 <= m then
    begin
      WriteLn(IntToStr(n));
      Dec(m);
    end;
  end;
end.

もうすこし調べたら、そのものずばりの関数がありました。がっかり。

出力例:
>> randsample(100,10)
ans =
    96
     6
    71
    46
    81
    56
    34
    22
    80
    88
1
randsample(n,m)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
math.randomseed(os.time())

function iota(n)
  local t = {}
  for i=1,n do
    table.insert(t, i)
  end
  return t
end

function lot(m, n)
  function pick(winners, rest, m)
    if m == 0 then
      return winners
    else
      local picked = table.remove(rest, math.random(#rest))
      table.insert(winners, picked)
      return pick(winners, rest, m-1)
    end
  end
  return pick({}, iota(n), m)
end

function show_array(t)
  print("["..table.concat(t, " ").."]")
end

show_array(lot(arg[1], arg[2]))

(lot 100 3)
=>(99 21 16)
1
2
3
(defun lot (n m)
  (let ((v (vconcat (number-sequence 0 (- n 1)))))
    (nthcdr (- n m) (append (shuffle-vector v) nil))))

勉強し始めているので、とりあえず投稿
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private function execute(n:int, m:int):void {
        var res:Array = [];
        for (var i:int = 0; i < m;) {
                var r:int = Math.floor(Math.random() * n);
                if (res.indexOf(r) >= 0) continue;
                i++;
                res.push(r);
        }
        Alert.show(res.toString());
}

基本的なリスト操作述語が無いので、長たらしくなります。
リスト生成→乱打舞頭→頭から順番にN個という流れです。
randomizeは、M人分だけすれば良い気もします。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
serial(S,E,[]):-S > E.
serial(S,E,[S|R]):-succ(S,S1),serial(S1,E,R).

remove([L|Ls],1,L,Ls).
remove([],_,_,_):-fail.
remove([L|Ls],N,E,[L|R]):-succ(N1,N),remove(Ls,N1,E,R).

randomize([X],[X]).
randomize(L,R):-random(N),length(L,Ll),Nx is floor(N * Ll) + 1,remove(L,Nx,Le,Rs),randomize(Rs,Rss),R=[Le|Rss].

take(_,0,[]).
take([L|Ls],N,[L|R]):-succ(N1,N),take(Ls,N1,R).

lot(N,M,R):-serial(1,N,L0),randomize(L0,L1),take(L1,M,R).

:-lot(20,5,R),writeln(R),halt.

#1248は無かったことに。 Nまでの列生成→ランダムにM個取り出し です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
serial(S,E,[]):-S > E.
serial(S,E,[S|R]):-succ(S,S1),serial(S1,E,R).

remove([L|Ls],1,L,Ls).
remove([L|Ls],N,E,[L|R]):-succ(N1,N),remove(Ls,N1,E,R).

randomize(0,_,[]):-!.
randomize(I,L,R):-random(N),length(L,Ll),Nx is floor(N * Ll) + 1,remove(L,Nx,Le,Rs),succ(I1,I),
                  randomize(I1,Rs,Rss),R=[Le|Rss].

lot(N,M,R):-serial(1,N,L0),randomize(M,L0,R).

:-lot(20,5,R),writeln(R),halt.

元の配列には影響を与えない
alert(["ほ","げ","foo","bar"].cnr(2));
1
2
3
4
Array.prototype.cnr = function(n)
{
	return this.sort(function(){return Math.random()-0.5}).slice(0,n-this.length);
}


	
1
2
3
4
5
6
import java.util.*
function lot(n, m){
  l = list(range(0,n-1))
  Collections.shuffle(l)
  l[0..m-1]
}

gawk -f lottery.awk <m> <n> [seed]
として使うと、0~(m-1)の数字からランダムでn個表示します。
seedはオプションです。

ex) gawk -f lottery.awk 10 3
0
4
7

任意の数字が選ばれる確率は、(残りの当選数)/(残りの総数)となります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
BEGIN {
	m = ARGV[1]
	n = ARGV[2]
	if(ARGC == 4) {
		srand(ARGV[3])
	} else {
		srand()
	}

	for(i = 0; i < m; i++) {
		if(rand() * (m - i) < n) {
			print i
			n--
		}
		if(n == 0) {
			break
		}
	}
}

mがnに近くなると効率悪くなりますが。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Public Sub DrawLots(ByVal n As Integer, ByVal m As Integer)
    If n < m OrElse n < 1 Then
        Exit Sub
    End If

    Dim list As New List(Of Integer)
    Dim r As New Random

    Do
        Dim v As Integer = r.Next(1, n + 1)
        If Not list.Contains(v) Then
            list.Add(v)
            If list.Count = m Then
                Exit Do
            End If
        End If
    Loop

    list.Sort()

    For Each i As Integer In list
        Console.WriteLine(i)
    Next
End Sub

SWI-Prologです。

長さnのリストからm個をランダムに選びます。
リストの最初の要素をm/nの確立で選び、
    選ばれた       ⇒ 残りの長さn-1のリストからm-1個を選ぶ
    選ばれなかった ⇒ 残りの長さn-1のリストからm個を選ぶ
という再帰的なアルゴリズムです。
結局は普通のランダム・サンプリングと同じです。

これだとリストでも計算量が線形時間になります。

実行例(n=100, m=5の場合):
:- numlist(1, 100, L), sample(L, 5, Samples).
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
Samples = [1, 6, 36, 68, 85] 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
%%	sample(+List1, +N, -List2)
%
%	List2はList1からランダムにN個の要素を選んだもの

sample(Xs, N, Ys) :-
	length(Xs, Len),
	N1 is max(0, min(N, Len)),
	sample_aux(Xs, Len, N1, Ys).

sample_aux(_, _, 0, []) :- !.
sample_aux([X|Xs], Len, N, [X|Ys]) :-
	random(Len) < N, !,
	Len0 is Len - 1,
	N0 is N - 1,
	sample_aux(Xs, Len0, N0, Ys).
sample_aux([_|Xs], Len, N, Ys) :-
	Len0 is Len - 1,
	sample_aux(Xs, Len0, N, Ys).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def lottery(n,m) {
  rand = new Random()

  all = 0..<n
  (1..m).inject([]) { wins, _bottom ->
    win = all[rand.nextInt(all.size())]
    all -= win
    wins + win
  }
}

println lottery(4,1)
println lottery(4,2)
println lottery(4,3)
println lottery(4,4)

ナイーブに。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import System.Environment
import System.Random

main :: IO ()
main = do { (p:q:_) <- getArgs
          ; let (n,m) = (read p,read q)
          ; newStdGen >>= loop n m [1..n] >>= print
          }

loop _ 0 _ _ = return []
loop n m s g 
 = case randomR (0,n-1) g of
    (n',g') -> case splitAt n' s of
                (xs,y:ys) -> loop (n-1) (m-1) (xs++ys) g' >>= return . (y:)


	
1
2
use List::Util qw/shuffle/;
sub lot {($n,$m)= @_; (shuffle(1..$n))[1..$m];}


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def kujibiki(users, m)
	raise ArgumentError unless users.size >= m 

	results=[]
	m.times{
		v=((users.size * rand()).to_i)
		results << users.delete_at(v)
	}
	results
end

users = [0,1,2,3,4,5,6,7,8,9]
p kujibiki(users, 10)

GPU向けの言語であるCUDAで作成してみました。
誰もうれしくないと思いますが、こんなのもあるんだね、とでも思って下さい。

CUDAに乱数生成関数がないため、本家サンプルの乱数生成を利用しています。

コア部分のアルゴリズムは#54の解釈を利用したので超簡単ですが、GPU駆動のコードのため伸びまくりです。かっこ悪い。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
float *d_Rand; // GPU上で利用する変数
float *h_Rand=NULL; // CPU上で利用する変数

// GPU上で乱数を生成するための関数。中身は本家サンプルを利用しているので割愛。
// d_Rand/h_Randに乱数がぶち込まれると解釈してください。
void initMTGPU()
{
  CUDA_SAFE_CALL( cudaMalloc((void **)&d_Rand, RAND_N * sizeof(float)) );
  RandomGPU<<<32, 128>>>(d_Rand, N_PER_RNG, SEED);
  CUT_CHECK_ERROR("RandomGPU() execution failed\n");
  CUDA_SAFE_CALL(cudaMallocHost((void**)&h_Rand, sizeof(float)*RAND_N));
  CUDA_SAFE_CALL(cudaMemcpy(h_Rand, d_Rand, sizeof(float)*RAND_N, cudaMemcpyDeviceToHost));
}

// コア部分。#54の解釈を利用。乱数の種は適当に設定。
#define RAND_SEED 5
__global__ void gpu(float *d_Random, int *answer, int n, int m)
{
  int i, j=0;
  int begin = d_Random[RAND_SEED] * (float)n;
  for(i=begin; i<begin+m; i++){
    answer[j++] = i % n;
  }
}

// くじ引き関数。answerに答えの数列が格納されます。
int lot(int *answer, int n, int m)
{
  CUT_DEVICE_INIT();

  initMTGPU();
  int *d_mem;
  CUDA_SAFE_CALL(cudaMalloc((void**)&d_mem, sizeof(int)*m));
  CUDA_SAFE_CALL(cudaMemcpy(d_Rand, h_Rand, sizeof(float)*RAND_N, cudaMemcpyHostToDevice));
  dim3 threads(1, 1, 1);
  dim3 grid(1,1,1);
  gpu<<< grid, threads >>>(d_Rand, d_mem, n, m);
  CUDA_SAFE_CALL( cudaThreadSynchronize() );
  CUT_CHECK_ERROR("Kernel execution failed");
  CUDA_SAFE_CALL(cudaMemcpy(answer, d_mem, sizeof(int)*m, cudaMemcpyDeviceToHost));

  CUDA_SAFE_CALL(cudaFree(d_mem));
  CUDA_SAFE_CALL(cudaFreeHost(h_Rand));
  return 0;
}

#define N_ALLSIZE 100
#define N_HITSIZE 5

int main(int argc, char** argv)
{
  int answer[N_HITSIZE];
  lot(answer, N_ALLSIZE, N_HITSIZE);

  int i;
  for(i=0; i<N_HITSIZE; i++){
    printf(" %d", answer[i]);
  }
  printf("\n");
}

綺麗じゃないけど。。。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fun choice n m =
let
  val rnd = Random.rand (Time.toNanoseconds (Time.now ()), m)
  fun loop 0 m' = m'
    | loop n' m' =
  let
    val number = Random.randRange (1, n) rnd
  in
    if List.exists (fn x => x = number) m' then loop n' m' else loop (n' - 1) (number::m')
  end
in
  loop m []
end


円順列から切り取る方式。
1
2
3
4
5
6
7
どう書く4=「|n m|
 「|候補 当選 番|
  「|x|候補!(x)入れる」!(n)繰り返す。
  「m>0」!の間「番=番%n+1。m=m-1。当選!(候補!(番)見る)入れる」実行。
  」!(配列!作る)(配列!作る)(乱数(n))実行」。

ラベル!(どう書く4!13 11 実行)作る。

アレイのuniqと似てるなぁ。
ちなみにn < mだと全員当選します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#import <Foundation/Foundation.h>
#import <stdio.h>

@interface NSArray (Random)
- (NSArray *)objectsAtRandom:(int)number;
@end

@implementation NSArray (Random)
- (NSArray *)objectsAtRandom:(int)m
{
    int n = [self count];
    id selected;
    if (m >= n)
    {
        selected = self;
    }
    else
    {
        srand(time(nil));
        NSMutableDictionary* winnerTable = [NSMutableDictionary dictionary];
        selected = [NSMutableArray array];
        id winner;
        while ([selected count] < m)
        {
            winner = [self objectAtIndex:(rand()%n)];
            if (![winnerTable objectForKey:winner])
            {
                [winnerTable setObject:winner forKey:winner];
                [selected addObject:winner];
            }
        }
    }
    return selected;
}
@end

int main(int argc, const char * argv[])
{
    NSAutoreleasePool* pool = [NSAutoreleasePool new];

    if (argc < 3) {
        printf("Usage: %s n player1[,player2...]\n", argv[0]);
        return 1;
    }

    NSArray* players = [[NSString stringWithCString:argv[2] encoding:NSUTF8StringEncoding] componentsSeparatedByString:@","];
    for (id winner in [players objectsAtRandom:atoi(argv[1])])
    {
        printf("%s\n", [winner cStringUsingEncoding:NSUTF8StringEncoding]);
    }

    [pool release];
    return 0;
}

1
2
3
4
5
6
7
8
100から10をくじ引きして表示
●くじ引き(mからnを)
    t1とは配列;t2とは配列
    もし(n>m)ならば,""で戻る
    (m)回,t1[回数-1]=回数-1
    t1を配列シャッフル
    (n)回,t2[回数-1]=t1[回数-1]
    t2で戻る

yield句の便利さを最近知りました。
yield句を用いると、この手のコードを簡単にかけますね。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections;

class Program
{
    static void Main(string[] args) 
    {
        int n = 10, m = 4;
        foreach( var val in GetIterator(n,m) ) Console.WriteLine( val );
    }
    public static System.Collections.IEnumerable GetIterator( int n, int m)
    {
        Random r = new Random();
        ArrayList list = new ArrayList();
        for(int i=1; i<=n ; i++) list.Add( i );
        for(int i=0; i<m ; i++ )
        {
            var temp = list[r.Next(list.Count)];
            list.Remove(temp);
            yield return temp;
        }
    }
}

j言語です。二項演算子として使います。
   10 f 5
9 5 7 10 1

   10 f 5
3 8 4 6 2

   10 f 5
6 10 4 7 9

   10000 f 5
7973 8054 1191 450 7334
1
f=.4 :'>:y?x'

長さNのリストをシャッフルして、先頭からM個を取り出します。

1> c(n_m).
{ok,n_m}
2> n_m:n_m(10, 3).
[3,1,9]
3> n_m:n_m(10, 3).
[1,6,7]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-module(n_m).
-export([n_m/2]).

shuffle(List, 0) -> List;
shuffle(List, N) ->
        R = lists:nth(random:uniform(length(List)), List),
        L = [R | [X || X <- List, X =/= R]],
        shuffle(L, N - 1).

n_m(N, M) -> lists:sublist(shuffle(lists:seq(1, N), N), M).

Ioを使ってみる.

1
lot := method(n, m, 1 to(n) asList shuffle slice(0, m))

Integerの動的配列をtypeで定義しています。
Delphiは配列操作が苦手なので、できるだけ使わないようにしています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type
  TDynIntArray = array of Integer;

function lot(n, m: Integer): TDynIntArray;
  function NumInArray(num: Integer; arr :TDynIntArray): Boolean;
  var
    x: Integer;
  begin
    Result := False;
    for x in arr do
      if x = num then
        Result := True;
  end;
var
  people: TDynIntArray;
  i: Integer;
  num_lot: Integer;
begin
  SetLength(people, m);
  Randomize;
  for i := 0 to m - 1 do
  begin
    repeat
      num_lot := Random(n) + 1;
    until not NumInArray(num_lot, people);
    people[i] := num_lot;
  end;
  Result := people;
end;

選んだ数が、結果の配列になければ追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
n = 10
m = 3
results = []

if 1 <= m && m <= n
  i = 0
  while i < m
    chosen_one = rand(n) + 1
    unless results.include?(chosen_one)
      results << chosen_one
      i += 1
    end
  end
  p results.sort
else
  puts "mは1以上n以下で指定してください。"
end

Arcです。
お題46の「重複無し乱数」で定義したbingoを使用しています。
(lot 999 4)
;=> (484 286 880 186)
;...
;=> (699 151 312 134) 
1
2
(def lot (n m)
  (firstn m (bingo n)))

動作環境 Fedora7 Perl5.8.8

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/perl
use strict;

exit main();

sub main{
        if(@ARGV != 2){
                print "usage: perl 3360.pl <total-number> <target-number>\n";
                return 1;
        }

        my $rtn = 0;

        if($rtn = is_num($ARGV[0])){
                print "$ARGV[0] not integer\n";
                return $rtn;
        }

        if($rtn = is_num($ARGV[1])){
                print "$ARGV[1] not integer\n";
                return $rtn;
        }

        if($ARGV[0] <= $ARGV[1]){
                print "$ARGV[0] isn't greater than $ARGV[1]\n";
                return 3;
        }

        my $total = $ARGV[0];
        my $target = $ARGV[1];

        my $rand_numbers = get_rand_number($total,$target);

        print join "\n", @$rand_numbers;

        return 0;
}

sub is_num{
        my ($num) = shift;

        if($num !~ / [0-9]+$/){
                return 2;
        }

        return 0;
}

sub get_rand_number{
        my ($total,$target) = @_;

        my $numbers = [];
        my $rand = undef;

        for(my $i=0; $i<$target; $i++){
                $rand = int(rand($total))+1;

                if(!grep { /^$rand$/ } @$numbers){
                        push @$numbers, $rand;
                }
                else{
                        redo;
                }
        }

        return $numbers;
}

n 人中1人を抜き出し、更に残りから 1人を抜きだし、を繰り返しです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
%!PS

/Kuji { % n m Kuji [numbers]
    [ 3 1 roll
    [
        1 1 5 -1 roll { } for
        dup 2 add -1 roll
        {
            counttomark dup rand exch mod roll
            counttomark 1 add 1 roll
        } repeat
    ] pop
    ]
} bind def

% ----- Test Code ---------
100 20 Kuji ==

とりあえず。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function lot($n, $m) {
  if ($n -ge $m) {
    $randomgenerator = New-Object Random
    $source = 1 .. $n
    $limit = $n
    $result = @()
    foreach ($i in 1 .. $m) {
      $limit -=  1
      $ii = $randomgenerator.Next($limit + 1)
      $result += $source[$ii]
      if ($ii -gt 0) {
        $srclow = $source[0 .. ($ii - 1)]
      } else {
        $srclow = @()
      }
      if ($ii -lt $limit) {
        $srchi = $source[($ii + 1) .. $limit]
      } else {
        $srchi = @()
      }
      $source = $srclow + $srchi
    }
  }
  return $result | sort
}

random.shuffleは便利ですが、それだと味気ないので‥‥。

Rubyの「Enumerable#sort_by { rand }」と同じ発想でシャッフルしてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import random
import sys

def lot(n, m):
  a = [(x, random.random()) for x in range(1, n + 1)]
  a.sort(lambda x, y: cmp(x[1], y[1]))
  return [x[0] for x in a][0:m]

if len(sys.argv) != 3:
  print "usage: %s all pickup" % (sys.argv[0])
else:
  print lot(int(sys.argv[1]), int(sys.argv[2]))

Limbo習作。
rand, daytime, stringモジュールのテスト。

すでにLimboでいっぱい書いている方がいるようなので、遠慮がちに・・・
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# file: d4.b
# Lottery

implement d4;

include "sys.m";
include "draw.m";
include "string.m";
include "rand.m";
include "daytime.m";

sys: Sys;
usage: fn(prog: string);

d4: module
{
    init: fn(ctxt: ref Draw->Context, argv: list of string);
};

init (ctxt: ref Draw->Context, argv: list of string)
{
    str: String;
    rand: Rand;
    time: Daytime;
    n, m, i, val: int;
    prog: string;

    sys = load Sys Sys->PATH;
    str = load String String->PATH;
    rand = load Rand Rand->PATH;
    time = load Daytime Daytime->PATH;

    prog = hd argv;

    argv = tl argv;
    if(argv == nil){
        usage(prog);
        return ;
    }
    (n, nil) = str->toint(hd argv, 10);

    argv = tl argv;
    if(argv == nil){
        usage(prog);
        return;
    }
    (m, nil) = str->toint(hd argv, 10);

    if(m == 0 || n == 0 || n < m){
        usage(prog);
        return;
    }

    flags := array [n] of int;
    for(i = 0; i < n; i++){
        flags[i] = 0;
    }

    rand->init(time->now());
    while(m > 0){
        val = rand->rand(n);
        if(flags[val] == 0){
            flags[val] = 1;
            m --;
        }
    }

    for(i = 0; i < n; i++){
        if(flags[i]){
            sys->print("%d ", i + 1);
        }
    }
    sys->print("\n");
}

usage (prog: string)
{
    sys->print("usage: %s n m\n", prog);
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
function randsample() {
    local n=$1
    local m=$2
    local -a array
    local -i i j tmp

    for ((i = 0; i < n; i++)) {
        array[i]=$i
    }

    # shuffle
    for ((i = 0; i < n; i++)) {
        ((j = RANDOM % (i + 1),
          tmp = array[i], array[i] = array[j], array[j] = tmp))
    }

    # print top m
    for ((i = 0; i < m && i < n; i++)) {
        echo ${array[i]}
    }
}

randsample 100 10

GNU coreutilsのshufで。

1
2
3
4
5
6
#!/bin/sh
randsample() {
    seq $1 | shuf | head -n $2
}

randsample 100 10

m > n でも n > m でも引数が1つでも公平に。shuffle使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def kuji(n, m = n)
  m, n = n, m if m > n
  srand
  [*(1..n)].shuffle[0..(m-1)]
end

kuji(10, 3).sort # => [1, 2, 3]
kuji(3, 10).sort # => [2, 4, 6]
kuji(10, 10) # => [6, 7, 9, 1, 10, 4, 2, 3, 5, 8]
kuji(10) # => [10, 2, 8, 6, 1, 9, 4, 3, 5, 7]

こっちの方がRubyっぽいかしらん? IntegerクラスとRangeクラスに定義してみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Integer
  def kuji(m = self)
    srand
    [*(1..self)].shuffle.first(m)
  end
end

class Range
  def kuji(m = self)
    srand
    [*self].shuffle.first([*m].last)
  end
end

#Integer
10.kuji(3) # => [2, 6, 10]
10.kuji # => [9, 10, 6, 1, 2, 3, 4, 5, 8, 7]

#Range
(1..10).kuji(3) # => [5, 4, 2]
(1..10).kuji # => [9, 6, 7, 4, 8, 5, 1, 10, 2, 3]

いまさら投稿。

1
2
3
4
5
6
7
def select(n, m){
    def list = 1..n as ArrayList
    Collections.shuffle list
    list[0..<m]
}

println select(5, 3)

PHP勉強中
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
print <<< END_DOC
<HTML>
<HEAD><title>doukaku 04</title>
</HEAD><BODY>
END_DOC;

function doukaku04($n, $m)
{
    $a = array();
    for($i = 0; $i < $n; $i++){
        $r = mt_rand(0, $i - 1);
        array_push($a, $a[$r]);
        $a[$r] = $i;
    }
    
    return array_slice($a, 0, $m);
};

foreach(doukaku04(10, 5) as $d){
    print "$d,";
}

print <<< END_DOC
</BODY>
</HTML>
END_DOC;
?>

Index

Feed

Other

Link

Pathtraq

loading...