Pseudorandom number generator image

From Rosetta Code
Revision as of 22:59, 12 October 2023 by Xing216 (talk | contribs) (Added a python3 implementation and linked to the output.)
Pseudorandom number generator image is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

Write a program that creates an image from a Pseudorandom Number Generator (PRNG) algorithm's output. The image can have the following dimensions:

  1. 250px by 250px : If the algorithm requires the use of prime numbers, use 8-15 bit primes.
  2. 500px by 500px : If the algorithm requires the use of prime numbers, use 8-15 bit primes.
  3. 1000px by 1000px : If the algorithm requires the use of prime numbers, use 8-32 bit primes.
  4. 1500px by 1500px : If the algorithm requires the use of prime numbers, use 16-64 bit primes.
Possible Output

https://www.random.org/analysis/randbitmap-rdo.png

https://ibb.co/ZJPS16j

See also
  •   Blum Blum Shub [1].
  •   Blum-Micali Algorithm: [2].
  •   Linear congruential generator [3].



6502 Assembly

6502js/easy6502

The "hardware" gives us a memory-mapped port at address $00FE which contains a different random number every clock cycle. We can use this to write to video memory (there are only 16 colors so the top 4 bits of the random value are ignored.)

define vramPtr $00
define vramPtrHi $01
main:
;we're guaranteed to start off with all registers zeroed.
	STA vramPtr
	LDA #$02
	STA vramPtrHi
	LDX #4
	
loop:
	LDA $FE	;read a random byte from the port
	STA (vramPtr),y
	INY
	BNE loop
	INC vramPtrHi
	DEX
	bne loop
	
	brk    ;end program

Output can be seen by copying/pasting the above code here.

Ada

-- Generate a (pseudo)random image
-- J. Carter     2023 Apr
-- Uses Ada_GUI (https://github.com/jrcarter/Ada_GUI)

with Ada.Numerics.Discrete_Random;
with Ada_GUI;

procedure Random_Image is
   package Color_Random is new Ada.Numerics.Discrete_Random (Result_Subtype => Ada_GUI.RGB_Value);
   
   Gen   : Color_Random.Generator;
   Image : Ada_GUI.Widget_ID;
   Event : Ada_GUI.Next_Result_Info;
   
   use type Ada_GUI.Event_Kind_ID;
begin -- Random_Image
   Color_Random.Reset (Gen => Gen);
   Ada_GUI.Set_Up (Title => "Random Image");
   Image := Ada_GUI.New_Graphic_Area (Width => 250, Height => 250);
   
   All_X : for X in 0 .. 249 loop
      All_Y : for Y in 0 .. 249 loop
         Image.Set_Pixel (X => X, Y => Y, Color => (Red   => Color_Random.Random (Gen),
                                                    Green => Color_Random.Random (Gen),
                                                    Blue  => Color_Random.Random (Gen),
                                                    Alpha => 1.0) );
      end loop All_Y;
   end loop All_X;
   
   Wait : loop
      Event := Ada_GUI.Next_Event;
      
      exit Wait when not Event.Timed_Out and then Event.Event.Kind = Ada_GUI.Window_Closed;
   end loop Wait;
   
   Ada_GUI.End_GUI;
end Random_Image;
end.
Output:

Media:Random_Image.png

Delphi

program Pseudorandom_number_generator_image;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  vcl.Graphics,
  Vcl.Imaging.PngImage;

type
  TRGBTriple = packed record
    b: Byte;
    g: Byte;
    r: Byte;
  end;

  PRGBTripleArray = ^TRGBTripleArray;

  TRGBTripleArray = array[0..999] of TRGBTriple;

function Noise(cWidth, cHeight: Integer; Color: boolean = True): TBitmap;
const
  Seed = 2147483647;
var
  Pixels: PRGBTripleArray;
begin
  RandSeed := Seed;
  Result := TBitmap.Create;
  with Result do
  begin
    SetSize(cWidth, cHeight);
    PixelFormat := pf24bit;
    for var row := 0 to cHeight - 1 do
    begin
      Pixels := ScanLine[row];
      for var col := 0 to cWidth - 1 do
      begin
        if Color then
        begin
          Pixels[col].r := random(255);
          Pixels[col].g := random(255);
          Pixels[col].b := random(255);
        end
        else
        begin
          var Gray := Round((0.299 * random(255)) + (0.587 * random(255)) + (0.114
            * random(255)));
          Pixels[col].r := Gray;
          Pixels[col].g := Gray;
          Pixels[col].b := Gray;
        end;
      end;
    end;
  end;
end;

const
  cWidth = 1000;
  cHeight = 1000;

begin
  // Color noise
  var bmp := Noise(cWidth, cHeight);
  bmp.SaveToFile('randbitmap-rdo.bmp');

  // to Png
  with TPngImage.create do
  begin
    Assign(bmp);
    SaveToFile('randbitmap-rdo.png');
    free;
  end;
  bmp.Free;

  // Gray noise
  bmp := Noise(cWidth, cHeight, False);
  bmp.SaveToFile('randbitmap-rdo_g.bmp');

  // to Png
  with TPngImage.create do
  begin
    Assign(bmp);
    SaveToFile('randbitmap-rdo_g.png');
    free;
  end;
  bmp.Free;

end.
Output:

randbitmap-rdo.png.

Factor

Factor's default PRNG is Mersenne Twister, but it can be easily swapped out for others like Drand, Xoroshiro, Blum Blum Shub, lagged Fibonnaci, system RNGs, and more.

Works with: Factor version 0.99 2021-02-05
USING: accessors images.testing images.viewer literals math
random sequences ;

CONSTANT: size 500

<rgb-image>
  ${ size size } >>dim
  size sq 3 * [ 256 random ] B{ } replicate-as >>bitmap
image-window

Forth

Works with: gforth version 0.7.3

Uses gforth random generator to create PBM portable pixmap image file.

require random.fs
: prngimage
outfile-id >r
s" prngimage.pbm" w/o create-file throw to outfile-id
s\" P1\n500 500\n" type
500 0 do
  500 0 do
    2 random 48 + emit
  loop  #lf emit
loop
outfile-id close-file throw
r> to outfile-id ;

prngimage


FreeBASIC

Windowtitle "Pseudorandom number generator image"
Dim As Integer w = 500, h = w, x, y
Screenres w, h, 16

For x = 0 To w-1
    For y = 0 To h-1
        Pset(x, y), Rgb(Rnd * 255, Rnd * 255, Rnd * 255)
    Next y
Next x

Bsave "Pseudo-Random-Algorithm.bmp",0

image500.png (sample image, offsite)


Go

The math/rand package uses a custom algorithm attributed to D.P.Mitchell and J.A.Reeds. It doesn't need to be seeded by a prime number. Typically (as here) the seed is generated from the current time.

The image is saved to a .png file which can then be viewed with a utility such as EOG.

package main

import (
    "image"
    "image/color"
    "image/png"
    "log"
    "math/rand"
    "os"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    img := image.NewNRGBA(image.Rect(0, 0, 1000, 1000))
    for x := 0; x < 1000; x++ {
        for y := 0; y < 1000; y++ {
            col := color.RGBA{uint8(rand.Intn(256)), uint8(rand.Intn(256)), uint8(rand.Intn(256)), 255}
            img.Set(x, y, col)
        }
    }
    fileName := "pseudorandom_number_generator.png"
    imgFile, err := os.Create(fileName)
    if err != nil {
        log.Fatal(err)
    }
    defer imgFile.Close()

    if err := png.Encode(imgFile, img); err != nil {
        imgFile.Close()
        log.Fatal(err)
    }
}

Java

Following implementation generates images from java.util.Random(uses linear congruential generator [4].) and Blum Blum Shub Algorithm with least significant bit method and even bit parity method[5].

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Random;
import java.util.Scanner;
/*
* Numbers to try:
* p = 11 or BigInteger.probablePrime(BIT1_LENGTH, rand)
* q = 23 or BigInteger.probablePrime(BIT_LENGTH, rand)
* seed = 3 or BigInteger.probablePrime(BIT_LENGTH,rand)
* */

public class csprngBBS {
    public static Scanner input = new Scanner(System.in);
    private static final String fileformat = "png";
    private static String bitsStri = "";
    private static String parityEven = "";
    private static String leastSig = "";
    private static String randomJavaUtil = "";
    private static int width = 0;
    private static int BIT_LENGTH = 0;
    private static final Random rand = new SecureRandom();
    private static BigInteger p = null; // 11
    private static BigInteger q = null; // 23
    private static BigInteger m = null;
    private static BigInteger seed = null; // 3
    private static BigInteger seedFinal = null;
    private static final Random randMathUtil = new SecureRandom();
    public static void main(String[] args) throws IOException {
        System.out.print("Width: ");
        width = input.nextInt();
        System.out.print("Bit-Length: ");
        BIT_LENGTH = input.nextInt();
        System.out.print("Generator format: ");
        String useGenerator = input.next();
        p = BigInteger.probablePrime(BIT_LENGTH, rand);
        q = BigInteger.probablePrime(BIT_LENGTH, rand);
        m = p.multiply(q);
        seed = BigInteger.probablePrime(BIT_LENGTH,rand);
        seedFinal = seed.add(BigInteger.ZERO);
        if(useGenerator.contains("parity") && useGenerator.contains("significant")) {
            findLeastSignificant();
            findBitParityEven();
            createImage(parityEven, "parityEven");
            createImage(leastSig, "significant");
        }

        if(useGenerator.contains("parity") && !useGenerator.contains("significant")){
            findBitParityEven();
        }

        if(useGenerator.contains("significant") && !useGenerator.contains("parity")){
            findLeastSignificant();
            createImage(leastSig, "significant");
        }

        if(useGenerator.contains("util")){
            findRandomJava(randMathUtil);
            createImage(randomJavaUtil, "randomUtilJava");
        }
    }
    public static void findRandomJava(Random random){
        for(int x = 1; x <= Math.pow(width, 2); x++){
            randomJavaUtil += random.nextInt(2);
        }
    }

    public static void findBitParityEven(){
        for(int x = 1; x <= Math.pow(width, 2); x++) {
            seed = seed.pow(2).mod(m);
            bitsStri = convertBinary(seed);
            char[] bits = bitsStri.toCharArray();
            int counter = 0;
            for (char bit : bits) {
                if (bit == '1') {
                    counter++;
                }
            }
            if (counter % 2 != 0) {
                parityEven += "1";
            } else {
                parityEven += "0";
            }
        }
    }

    public static void findLeastSignificant(){
        seed = seedFinal;
        for(int x = 1; x <= Math.pow(width, 2); x++){
            seed = seed.pow(2).mod(m);
            leastSig += bitsStri.substring(bitsStri.length() - 1);
        }
    }

    public static String convertBinary(BigInteger value){
        StringBuilder total = new StringBuilder();
        BigInteger two = BigInteger.TWO;
        while(value.compareTo(BigInteger.ZERO) > 0){
            total.append(value.mod(two));
            value = value.divide(two);
        }
        return total.reverse().toString();
    }

    public static void createImage(String useThis, String fileName) throws IOException {
        int length = csprngBBS.width;
        // Constructs a BufferedImage of one of the predefined image types.
        BufferedImage bufferedImage = new BufferedImage(length, length, 1/*BufferedImage.TYPE_INT_RGB*/);
        // Create a graphics which can be used to draw into the buffered image
        Graphics2D g2d = bufferedImage.createGraphics();
        for (int y = 1; y <= length; y++) {
            for (int x = 1; x <= length; x++) {
                if (useThis.startsWith("1")) {
                    useThis = useThis.substring(1);
                    g2d.setColor(Color.BLACK);
                    g2d.fillRect(x, y, 1, 1);
                } else if (useThis.startsWith("0")) {
                    useThis = useThis.substring(1);
                    g2d.setColor(Color.WHITE);
                    g2d.fillRect(x, y, 1, 1);
                }
            }
            System.out.print(y + "\t");
        }
        // Disposes of this graphics context and releases any system resources that it is using.
        g2d.dispose();
        // Save as file
        File file = new File("REPLACEFILEPATHHERE" + fileName + "." + fileformat);
        ImageIO.write(bufferedImage, fileformat, file);
    }
}

jq

Works with jq and gojq, the C and Go implementations of jq

It has been claimed that the elementary cellular automaton with "Rule 30" can be used as a PRNG, (see e.g. Elementary_cellular_automaton/Random_number_generator), so this entry generates a set of (x,y,color) co-ordinates so that this hypothesis might be visually evaluated e.g. using the gnuplot program.

To keep things brief, the jq filter definitions at Elementary_cellular_automaton#jq are used but not repeated here.

include "elementary-cellular-automaton" {search : "."};  # the defs at [[Elementary_cellular_automaton#jq]]

def binary2number:
  reduce (.[]|tonumber) as $x ({p:1}; .n += .p * $x | .p *= 2) | .n;

# Emit a stream of $n PRNGs in range(0;255)
def prng($n):
  # 30 is 11110
  ("1" + 100 * "0" )
  | [automaton(30; 8 * $n) | .[0:1]]
  | _nwise(8) | binary2number ;  

foreach prng(99*99) as $color ({x:0, y:1};
  .color = $color
  | .x += 1
  | if .x == 100 then .x = 1 | .y += 1 else . end )
  | "\(.x) \(.y) \(.color)"

Invocation:

jq -nrf program.jq > prng.txt
gnuplot
plot("prng.txt") with image pixels

Julia

Julia uses the Mersenne Twister algorithm for its default rand() function. That algorithm uses over 600 32-bit ints to represent its internal state, rather than just a product of two or three primes.

using FileIO, ImageIO

save("randombw.png", rand(Float16, 1000, 1000))

Lua

Lua uses the xoroshiro256** algorithm.

size = 500
math.randomseed(os.time())

-- Writes a 256-bit grayscale PGM image file:
function writePgm(data, fn, comment)
  local rows = #data
  local cols = #data[1]
  local file = io.open(fn, "wb")

  -- Write header in ASCII
  file:write("P5", "\n")
  if comment ~= nil then
    file:write("# ", comment, "\n")
  end
  file:write(cols, " ", rows, "\n")
  file:write("255", "\n")
  -- Write data in raw bytes
  for _, r in ipairs(data) do
    file:write(string.char(unpack(r)))
  end
  file:close()
end

img = {}
for r = 1, size do
  img[r] = {}
  for c = 1, size do
    img[r][c] = math.random(0,255)
  end
end

writePgm(img, "prng_img.pgm", string.format("PRNG Image (%d x %d)", size, size))

Maxima

genmatrix(lambda([i,j],random(1000)),1000,1000)$
wxdraw2d(image(%,0,0,30,30));
File:PseudoRandomImageMaxima.png

Nim

Nim standard PRNG is an implementation of the xoroshiro128+ (xor/rotate/shift/rotate) algorithm which is extremely fast. The standard library provides a Mersenne Twister implementation too. For this task, we used the first one.

import random
import imageman

const Size = 500

randomize()
var image = initImage[ColorRGBU](Size, Size)
for x in 0..<Size:
  for y in 0..<Size:
    let color = ColorRGBU([rand(255).byte, rand(255).byte, rand(255).byte])
    image[x, y] = color

image.savePNG("prng_image.png", compression = 9)

Perl

Perl unified the PRNG with its own internal drand48() implementation on all platforms since v5.20.0. Without a manual srand, Perl by default source the seed from "/dev/urandom" if it is available so there shouldn't be any prime prerequisite.

use strict;
use warnings;
use GD;

my $img = GD::Image->new(500, 500, 1);

for my $y (0..500) {
        for my $x (0..500) {
                my $color = $img->colorAllocate(rand 256, rand 256, rand 256);
                $img->setPixel($x, $y, $color);
        }
}

open  F, "image500.png";
print F  $img->png;

image500.png (sample image, offsite)

Phix

Library: Phix/pGUI
-- demo\rosetta\Pseudorandom_number_generator_image.exw
without js -- IupSaveImage(), not possible from within a browser (though a "save" button might be?)
include pGUI.e
 
IupOpen()
integer w=250, h=w
sequence bw = repeat(0,w*h)
for x=0 to w-1 do
    for y=0 to h-1 do
        if rand(2)=2 then bw[x*h+y+1] = 255 end if
    end for
end for
Ihandle image = IupImage(w,h,bw)
object res = IupSaveImage(image,"bw.png","PNG")
IupClose()

PicoLisp

(seed (in "/dev/urandom" (rd 8)))
(out "image.pbm"
   (prinl "P1")
   (prinl 500 " " 500)
   (do 500
      (do 500
         (prin (if (rand T) 1 0)) )
      (prinl) ) )

Python

Libraries: Pillow, random

# pseudorandom number image generator by Xing216
from random import randbytes
from PIL import Image

size = 1500
x = bytes.fromhex(" ".join([randbytes(3).hex() for x in range(size*size)]))
img = Image.frombuffer('RGB', (size, size), x, 'raw', 'RGB', 0, 1)
img.show()

Output: rcXing216.png (transfer.sh)

Raku

MoarVM uses Mersenne Twister as its PRNG but a prime seeder is not mandatory.

# 20200818 Raku programming solution

use Image::PNG::Portable;

srand 2⁶³ - 25; # greatest prime smaller than 2⁶³ and the max my system can take

my @data = < 250 500 1000 1500 >;

@data.map: {
   my $o = Image::PNG::Portable.new: :width($_), :height($_);
   for ^$_ X ^$_ -> @pixel { # about 40% slower if split to ($x,$y) or (\x,\y)
      $o.set: @pixel[0], @pixel[1], 256.rand.Int, 256.rand.Int, 256.rand.Int
   }
   $o.write: "image$_.png" or die;
}
Output:
file image*.png
image1000.png: PNG image data, 1000 x 1000, 8-bit/color RGBA, non-interlaced
image1500.png: PNG image data, 1500 x 1500, 8-bit/color RGBA, non-interlaced
image250.png:  PNG image data, 250 x 250, 8-bit/color RGBA, non-interlaced
image500.png:  PNG image data, 500 x 500, 8-bit/color RGBA, non-interlaced

image500.png (sample image, offsite)

Sidef

Translation of: Perl
require('GD')

var img = %O<GD::Image>.new(500, 500, 1)

for y in (0..500), x in (0..500) {
    var color = img.colorAllocate(255.irand, 255.irand, 255.irand)
    img.setPixel(x, y, color)
}

File("image500.png").write(img.png, :raw)

Wren

Library: DOME

Wren's 'random' module uses the 'Well equidistributed long-period linear' (WELL512a) PRNG which doesn't need to be seeded with a prime number. It is in fact seeded from a sequence of 16 numbers but, if less are provided, the others are generated automatically. Typically (as here) the seed is generated from the current time.

import "dome" for Window
import "graphics" for Canvas, Color
import "random" for Random

class Game {
    static init() {
        Window.title = "Pseudorandom Number Generator Image"
        Window.resize(1000, 1000)
        Canvas.resize(1000, 1000)
        var r = Random.new() // generates seed from current time
        for (x in 0...1000) {
            for (y in 0...1000) {
                var c = Color.rgb(r.int(256), r.int(256), r.int(256))
                Canvas.pset(x, y, c)
            }
        }
    }

    static update() {}

    static draw(dt) {}
}

XPL0

The PRNG is linear congruential and is built-in. It's seeded with the time-of-day.

int X, Y;
[SetVid($11B);  \VESA 1280x1024x24
for Y:= 0 to 1000-1 do
    for X:= 0 to 1000-1 do
        Point(X, Y, Ran($100_0000));
]
Output:
Essentially the same as Delphi's image.