How to pixelate an image - c

I am attempting to pixelate a P6 PPM format image via the following steps:
Read a PPM image in grids of 4x4
Find the average RGB colour value of each 4x4 grid
Write to a new file by setting each 4x4 grid of pixels in the new image to have the average RGB colour value.
The PPM file begins in the following format:
P6
# ignores comments in header
width
height
max colour value
My problem:
The output PPM image file (which I am opening using GIMP image editor, and can also be opened in any text editor like Notepad to view the raw data) consists of one flat colour block, when instead it should resemble a sort of mosaic.
Note: The 4x4 grid can be varied i.e. the higher the value of the grid dimensions, the more pixelated the output image becomes. My code has mostly been influenced from another Stack Overflow question where the user attempted a similar implementation in C#. The link to this question:
https://codereview.stackexchange.com/questions/140162/pixelate-image-with-average-cell-color
UPDATE: The output now seems to pixelate the first 1/5 of the image, but the rest of the output image remains one colour block. I think my issue lies in that I am treating the cells as the pixels in linear order.
My attempt:
#include <stdio.h>
#include <assert.h>
//Struct to store RGB values
typedef struct {
unsigned char r, g, b;
} pixel;
int main()
{
int y, x; //Loop iteration variables
int yy = 0; //Loop iteration variables
int xx = 0; //Loop iteration variables
char magic_number[10]; //Variable which reads P6 in the header
int w, h, m; //Image dimension variables
pixel currentPix; //Current pixel variable
int avR; //Red declaration
int avG; //Green declaration
int avB; //Blue declarataion
int total; //Loop iteration counter declaration
//Input file
FILE* f;
f = fopen("Dog2048x2048.ppm", "r"); //Read PPM file
if (f == NULL) //Error notifiaction if file cannot be found
{
fprintf(stderr, "ERROR: cannot open input file");
getchar();
exit(1);
}
//Scan the header of the PPM file to get the magic number (P6), width
//height and max colour value
fscanf(f, "%s %d %d %d", &magic_number, &w, &h, &m);
//initialize file for writing (open and header)
FILE* f_output;
f_output = fopen("some_file.ppm", "w");
//fprintf(f_output, "%s %d %d %d", magic_number, w, h, m);
fprintf(f_output, "P6\n%d %d\n255\n", w, h);
if (f_output == NULL) //Error notifiaction if file cannot be found
{
fprintf(stderr, "ERROR: cannot open output file");
getchar();
exit(1);
}
// Loop through the image in 4x4 cells.
for (int yy = 0; yy < h && yy < h; yy += 4)
{
for (int xx = 0; xx < w && xx < w; xx += 4)
{
avR = 0;
avG = 0;
avB = 0;
total = 0;
// Store each color from the 4x4 cell into cellColors.
for (int y = yy; y < yy + 4 && y < h; y++)
{
for (int x = xx; x < xx + 4 && x < w; x++)
{
//Reads input file stream
fread(&currentPix, 3, 1, f);
//Current pixels
avR += currentPix.r;
avG += currentPix.g;
avB += currentPix.b;
//Counts loop iterations for later use in colour averaging
total++;
}
}
//Average RGB values
avR /= total;
avG /= total;
avB /= total;
// Go BACK over the 4x4 cell and set each pixel to the average color.
for (int y = yy; y < yy + 4 && y < h; y++)
{
for (int x = xx; x < xx + 4 && x < w; x++)
{
//Print out to new file
fprintf(f_output, "%i %i %i\t", avR, avG, avB);
}
}
}
fprintf(f_output, "\n");
}
return 0;
}

Your main mistake is that you assume to be reading and writing 4×4 blocks of pixels while actually accessing pixel data linearly; your suspicion is correct.
Consider the following example. Let there be a 12×4 1-channel image:
01 02 03 04 05 06 07 08 09 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
Each of the pixels has the color that equals its position in a PPM file.
Now these are the pixels which you are expecting to read when pixelating the first 4×4 block:
01 02 03 04
13 14 15 16
25 26 27 28
37 38 39 40
And these are the pixels which are actually being read by sequentially executing fread():
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16
So eventually you are treating the input image as if it looked like that:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16 01 02 03 04 17 18 19 20 33 34 35 36
17 18 19 20 21 22 23 24 --> 05 06 07 08 21 22 23 24 37 38 39 40
25 26 27 28 29 30 31 32 09 10 11 12 25 26 27 28 41 42 43 44
33 34 35 36 13 14 15 16 29 30 31 32 45 46 47 48
37 38 39 40 41 42 43 44 45 46 47 48
instead of:
01 02 03 04 05 06 07 08 09 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
One of the simpler methods of resolving that issue is to allocate an array into which the data is to be read. Once you have that array filled with your data, you will be able to access its elements in any order, instead of the strictly linear order fread() implies.

Related

How to print a "neat" 2D array in C

I am able to print out a 2D array into an output file but it is not uniform. I would like to have the 2D array evenly spaced when it is printed out. I am fairly new at programing in C so any help would be greatly appreciated!
My Code:
#include <stdio.h>
#include <stdlib.h>
/**
* Driver method for program
*/
int main()
{
int nums[200];
int i = 0;
int j = 0;
FILE * in_file;
FILE * out_file;
srand(time(0)); //seed for random generator
/**
* if loop reads text file and stores numbers into array
*/
if (in_file = fopen("Data.txt", "r")) {
while (fscanf(in_file, "%d", &nums[i]) != EOF) {
i++;
}
int numbsinfile = i;
int random = randomNumber(2, 12);
int matrix1[5][random];
int matrix2[random][5];
out_file = fopen("out.txt", "w");
fprintf(out_file, "Matrix 1: \n");
fprintf(out_file, "Rows = 5 \n");
fprintf(out_file, "Columns = %d \n\n", random);
for(i = 0; i < 5; i++ ){
for(j = 0; j < random; j++){
int rand = randomNumber(0, numbsinfile);
matrix1[i][j] = nums[rand];
fprintf(out_file, "%d \t\t", matrix1[i][j]);
}
fprintf(out_file, "\n");
}
fclose(in_file);
fclose(out_file);
}
return 0;
}
/**
* Generates and prints random
* numbers in range [lower, upper].
*/
int randomNumber(int lower, int upper) {
int num = (rand() % (upper - lower + 1)) + lower;
return num;
}
The input file I am using along with the output file that my code produces. I am basically just wanting to clean up the 2D array that is printed to the output file.
input file:
23 34 -54 21 45 34 65 -54 21 45 34 65 -34 24 58
49 45 10 -57 20
57 39 20 58 23 10 20 58 -60 76 -82 28
28 -37 49 358 47 -50 37 29
57 -29 -20 47 69
93 57 23 49 -38 49 27 -40 48 39
56 -30 47 28 49
37 49
27 26 10 20 58 -60 26 10 20 58 -60 76 -82 28
28 -37 49 -28 93 28
73 47 27 83 37 -29 40 37 49 20
17 -26 12 17 17
18 38 29 39 -118
19 10 20 58 -60 76 -82 28
28 -37 49 59 10 58 -60 76 -82 28
28 -37 49 59 10 20 58 -60 76 -82 28
28 -37 49 30 -58 58 38 49 30 -58 58 38
49 30 -58 58 38
28 39
39 48 23 -50 28
48 29 39 40 29
My output file:
Matrix 1:
Rows = 5
Columns = 12
28 39 20 49 58 76 37 -26 47 -40 216309856 26
57 -50 30 47 29 58 73 20 26 216309856 49 26
216309856 30 59 45 20 23 -50 83 -50 -37 28 30
10 10 23 28 47 45 34 10 19 -38 -118 28
47 49 -40 20 49 29 10 20 58 69 10 28
How to print a “neat” 2D array in C (?)
Find the longest text width using snprintf(NULL, 0, some_format, ...
int width = 1;
for(i = 0; i < 5; i++ ) {
for(j = 0; j < random; j++) {
int rand = randomNumber(0, numbsinfile);
matrix1[i][j] = nums[rand];
int w = snprintf(NULL, 0, "%d", matrix1[i][j]);
if (w > width) width = w;
}
}
Use * in the specifier and width for printing.
for(i = 0; i < 5; i++ ) {
for(j = 0; j < random; j++) {
fprintf(out_file, " %*d", width, matrix1[i][j]);
}
fprintf(out_file, "\n");
}

How to print out the contents of an array non-linearly (e.g.in 4x4 cells)

I am attempting to print the contents of an array in a non-linear fashion (not sequentially). My code currently reads data from a text input, stores the text input data into a 2D array, and then sequentially iterates through the 2D array in 4x4 cells and prints the result to cmd.
Input file- test_file.txt:
01 02 03 04 05 06 07 08 09 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
The current undesired output:
001 002 003 004 013 014 015 016 025 026 027 028 037 038 039 040
005 006 007 008 017 018 019 020 029 030 031 032 041 042 043 044
009 010 011 012 021 022 023 024 033 034 035 036 045 046 047 048
The output I am trying to achieve:
01 02 03 04 05 06 07 08 09 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
My code attempt:
#include <stdio.h>
int main()
{
int h = 4; //Height of dataset
int w = 12; //Width of dataset
int matrix[4][12]; //The 2d matrix where the data is stored
int i, j; //Loop variables
FILE *f;
f = fopen("test_file.txt", "r"); //Input file
if (f == NULL)
{
fprintf(stderr, "Error: cannot open file input", f);
exit(1);
}
//Iterate through the input file stream
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
fscanf(f, "%d", &matrix[i][j]);
}
}
//Iterate through the 2d array in 4x4 cells
for (int ii = 0; ii < h && ii < h; ii += 4)
{
for (int jj = 0; jj < w && jj < w; jj += 4)
{
for (int i = ii; i < ii + 4 && i < h; i++)
{
for (int j = jj; j < jj + 4 && j < w; j++)
{
printf("%.3d ", matrix[i][j]);
}
}
printf("\n");
}
}
fclose(f);
getchar();
return 0;
}
EDIT: I unfortunately forgot to mention a key specification; I also would like to be able to vary the cell cize from 4x4 to other sizes e.g., 12x12.
Possible solution (printing loops only):
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
printf("%.2d ", matrix[i][j]);
if((j + 1) % 4 == 0) printf(" ");
}
printf("\n");
}
You can do as below.
//Iterate through the 2d array in 4x4 cells
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w ; j+=4)
{
for (int jj = 0; jj < h ; jj++)
{
printf("%.2d ", matrix[i][j+jj]);
}
printf(" ");
}
printf("\n");
}
or
//Iterate through the 2d array in 4x4 cells
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w ; j++)
{
printf("%.2d%s", matrix[i][j], ((j+1)%4 == 0)?" ":" ");
}
printf("\n");
}
Output:
01 02 03 04 05 06 07 08 09 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
If you really just want to print it, the following code would do that:
#include <stdio.h>
#define BLOCK 4
#define COLS 12
int main(int argc, char* argv[])
{
for(int i = 1; i <= 48; ++i) /* replace this loop with a loop over the actual data to print */
{
/* print mtx with columns and blocks
* insert a leading 0 with format %02i
* insert tab e.g. e.g. i == 4,8,12 etc.. for BLOCK=4, or a whitespace otherwise
* insert new line e.g. when i == 12, 24, etc... for COLS=12 */
printf("%02i%s%s", i, (i%BLOCK==0) ? "\t" : " ", (i%COLS==0) ? "\n" : "");
}
return 0;
}
http://coliru.stacked-crooked.com/a/1ca5b8796aea7224

Sorting sides of n triangles according to the area

Given n triangles, with sides a,b,c print them in the same style by sorting them from the smallest to the largest.
Complete problem: https://www.hackerrank.com/challenges/small-triangles-large-triangles/problem
In the solution, we have a structure named as Triangle. It has 3 integers a,b,c. An array of Triangle is made, named as tr and the input is passed to the function sort_by_area.
My approach is to apply bubble sort on this array.But instead of comparing tr[j] > tr[j+1] like we do in normal bubble sort, I'm comparing the areas of tr[j] and tr[j+1]. Now, if area of tr[j] > tr[j+1]: Swap.
The problem: In the end the results are wrong. The array doesn't sort properly. First I thought it's a typo somewhere so I rewrite the code but the problem persists.
double area (int a, int b, int c)
{
double p = (a+b+c)/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
void sort_by_area(triangle* tr, int n) {
/**
* Sort an array a of the length n
*/
int i,j;
double area1, area2;
triangle temp;
for(i = 0; i < n-1;++i)
{
for(j = 0; j < n-i-1; ++j)
{
area1 = area(tr[j].a , tr[j].b, tr[j].c);
area2 = area(tr[j+1].a , tr[j+1].b, tr[j+1].c);
if(area1 > area2)
{
temp = tr[j];
tr[j] = tr[j+1];
tr[j+1] = temp;
}
}
}
}
input: 20
23 37 47
22 18 5
58 31 31
28 36 40
54 62 11
31 41 14
53 18 54
41 38 55
55 44 44
44 48 18
26 41 65
20 23 21
58 61 50
28 56 56
20 39 32
33 45 49
26 41 62
31 46 39
48 49 67
57 33 45
expected output:
22 18 5
31 41 14
20 23 21
54 62 11
26 41 65
58 31 31
20 39 32
26 41 62
44 48 18
23 37 47
53 18 54
28 36 40
31 46 39
33 45 49
57 33 45
28 56 56
41 38 55
55 44 44
48 49 67
58 61 50
actual output:
22 18 5
54 62 11
31 41 14
20 23 21
26 41 65
20 39 32
58 31 31
26 41 62
23 37 47
44 48 18
53 18 54
28 36 40
31 46 39
33 45 49
57 33 45
28 56 56
41 38 55
55 44 44
48 49 67
58 61 50
This is my solution for this challenge:
double area(triangle t){
double p = (t.a+t.b+t.c)/2.0;
return sqrt(p*(p-t.a)*(p-t.b)*(p-t.c));
}
void sort_by_area(triangle* tr, int n) {
int i,j;
for(i=0;i<n-1;i++){
for(j=i+1;j<n;j++){
if(area(tr[j]) < area(tr[i])){
triangle temp = tr[i];
tr[i]=tr[j];
tr[j]=temp;
}
}
}
}
Your usage of the 'for' loop for comparing triangles is this (by triangle, I mean triangle's area):
for(i = 0; i < n - 1; ++i){
...
...
for(j = 0; j < n - i - 1; ++j){
...
}
}
Observe carefully: The loop you used fails to compare the last elements of the arrays.
I just improvised your code and it works:
#include < stdio.h >
#include < stdlib.h >
#include < math.h >
struct triangle
{
int a;
int b;
int c;
};
typedef struct triangle triangle;
float area(int a, int b, int c)
{
float p;
p = (float)(a + b + c) / 2;
return sqrt(p * (p - a) * (p - b) * (p - c));
}
void sort_by_area(triangle* tr, int n)
{
int i, j;
float area1, area2;
triangle temp[5];
for (i = 0; i < n - 1; i++)
{
for (j = 0; j < n - i - 1; j++)
{
area1 = area(tr[j].a, tr[j].b, tr[j].c);
area2 = area(tr[j + 1].a, tr[j + 1].b, tr[j + 1].c);
if (area1 > area2)
{
temp[0] = tr[j];
tr[j] = tr[j + 1];
tr[j + 1] = temp[0];
}
}
}
}
int main()
{
int n;
scanf("%d", &n);
triangle* tr = malloc(n * sizeof(triangle));
for (int i = 0; i < n; i++)
{
scanf("%d%d%d", &tr[i].a, &tr[i].b, &tr[i].c);
}
sort_by_area(tr, n);
for (int i = 0; i < n; i++)
{
printf("%d %d %d\n", tr[i].a, tr[i].b, tr[i].c);
}
return 0;
}
An edit of #Zümrüd-ü Anka's answer to match the bubble sorts you'll find elsewhere. Your loops actually should work fine, both for loops here are the same as yours. This passes the test cases for HackerRank. I think it was actually just the integer division that was giving you an issue.
double area(triangle t){
double p = (t.a+t.b+t.c)/2.0;
return sqrt(p*(p-t.a)*(p-t.b)*(p-t.c));
}
void sort_by_area(triangle* tr, int n) {
int i,j;
for(i=0;i<n-1;i++){
for(j=0;j<n-i-1;j++){
if(area(tr[j]) > area(tr[j+1])){
triangle temp = tr[j+1];
tr[j+1]=tr[j];
tr[j]=temp;
}
}
}
}

What's wrong with the matrix?

What's wrong with this code?
My task is: Create a square matrix of integers with a size of 9x9. Fill the matrix with random numbers. Display the main and side diagonal symmetrically with respect to the vertical axis. The example of expected result is here: matrix
Matrix :
20 20 76 65 93 76 16 2 85
6 87 78 43 48 81 71 90 38
10 12 35 77 48 88 24 53 7
12 66 51 35 74 7 30 22 49
58 14 71 46 68 68 10 81 51
98 16 74 47 64 25 17 30 37
2 44 44 74 34 54 86 73 28
85 4 57 75 18 28 51 76 2
35 17 53 76 15 91 83 85 72
The main and side diagonal:
85 20 76 65 93 76 16 2 20
6 90 78 43 48 81 71 87 38
10 12 24 77 48 88 35 53 7
12 66 51 7 74 35 30 22 49
58 14 71 46 68 68 10 81 51
98 16 74 25 64 47 17 30 37
2 44 86 74 34 54 44 73 28
85 76 57 75 18 29 51 4 2
72 17 53 76 15 91 83 85 35
But in fact the program prints only the main matrix with random numbers and after that stops.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <Windows.h>
int main()
{
int a = 9;
int matrix[a][a];
int temp;
int i, j, n;
srand((unsigned)time(NULL));
printf("Matrix : \n\n");
for (i = 0; i < a; ++i) {
for (j = 0; j < a; ++j) {
matrix[i][j] = rand() % 100;
printf("%d\t", matrix[i][j]);
}
printf("\n\n");
}
printf("The main and side diagonal:\n\n");
for (i = 0; i < a; ++i) {
temp = matrix[i][i];
matrix[i][i] = matrix[i][(a - 1) - i];
matrix[i][(a - 1) - i] = temp;
}
for (i = 0; i < a; ++i) {
for (j = 0; j < a; ++j) {
printf("Result:", matrix[i][j]);
printf("\n\n\n");
system("pause");
return 0;
}
}
}
You are returning where you are not supposed to. (in middle of the calculation). You should return after you end up working on the for loops.
for (i = 0; i < a; ++i) {
for (j = 0; j < a; ++j) {
printf("Result:", matrix[i][j]); <--- Not printing element
printf("\n\n\n");
system("pause");
return 0; <-----
}
}
It should be
for (i = 0; i < a; ++i) {
for (j = 0; j < a; ++j) {
printf("Result: %d ", matrix[i][j]); <----You forgot the
//format specifier
printf("\n\n\n");
system("pause");
}
}
return 0;<-----
Readability is hampered when the indentation is like this. You implemented wrong logic out of it.
OP asks that it stops after printing "Result" that is because you forgot to put the format specifier in the code. That's why none of the element is printed.
Op wants to print the main and side diagonal symmetrically with respect to the vertical axis.
Now this is everything to with the print part.
Now we have to find a way that will let us distinguish which one is diagonal element and which one is not.
Suprisingly the answer should be known to someone who is writing the previous swapping logic. (Though it is not clear why OP swapped it).
Now all element matrix[p][q] will be from either diagonal if p=q or p+q=a-1. (Note that matrix is a square matrix).
But OP meant to print the matrix
for (i = 0; i < a; ++i) {
if( i == 0) printf("The main and side diagonal : \n");
for (j = 0; j < a; ++j) {
printf("%d\t", matrix[i][j]);
}
printf("\n");
}
}
Use functions. You print the matrix twice; you should have a function to print the matrix which you call twice.
With such a function, you'd not make the mistakes in the tail end of your code. For example, you could use this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static void print_matrix(const char *tag, int size, int matrix[size][size])
{
printf("%s (%dx%d):\n\n", tag, size, size);
for (int i = 0; i < size; ++i)
{
const char *pad = "";
for (int j = 0; j < size; ++j)
{
printf("%s%-2d", pad, matrix[i][j]);
pad = " ";
}
printf("\n\n");
}
}
int main(int argc, char **argv)
{
unsigned seed = time(0);
int a = 9;
int matrix[a][a];
if (argc == 2)
seed = atoi(argv[1]);
srand(seed);
printf("Seed: %u\n", seed);
for (int i = 0; i < a; ++i)
{
for (int j = 0; j < a; ++j)
matrix[i][j] = rand() % 100;
}
print_matrix("Matrix", a, matrix);
for (int i = 0, j = a - 1; i < a; ++i, --j)
{
int temp = matrix[i][i];
matrix[i][i] = matrix[i][j];
matrix[i][j] = temp;
}
print_matrix("The main and side diagonal", a, matrix);
return 0;
}
The code reports the seed it uses; that allows you to reproduce any run by specifying the seed to use as a command line argument.
Example output:
Seed: 1511470282
Matrix (9x9):
11 39 3 88 98 63 75 81 76
93 9 60 22 45 50 46 58 65
13 99 25 43 14 57 44 70 65
30 57 55 0 37 84 47 49 40
60 28 46 1 96 78 33 20 9
93 61 11 38 84 16 91 26 15
43 85 66 72 85 39 96 45 45
45 25 33 3 78 90 61 65 62
88 84 56 34 74 8 78 57 74
The main and side diagonal (9x9):
76 39 3 88 98 63 75 81 11
93 58 60 22 45 50 46 9 65
13 99 44 43 14 57 25 70 65
30 57 55 84 37 0 47 49 40
60 28 46 1 96 78 33 20 9
93 61 11 16 84 38 91 26 15
43 85 96 72 85 39 66 45 45
45 65 33 3 78 90 61 25 62
74 84 56 34 74 8 78 57 88
The swapping process, in case it isn't obvious, swaps the first and last elements of the first row, the second and last but one element in the second row, and so on, forming an X of swapped elements.

Suggestion about unique values generation

I want to generate a series of "random" unique numbers, for use at a card game! These numbers should be between 0 and 81.
I don't care about security or speed at this stage, i just want something simple to have the work done.
In my code below, i have managed to create 2 unique random numbers in the array that holds them, but the rest 10 numbers don't change but stay -1 that was the initial value..
I have found more accurate ways for random number generation, but i will check them out at a later stage!
#include <stdio.h>
#include <stdlib.h>
int getRandomNumber(int Min, int Max)
{
double rnd= (double)rand()/((double)RAND_MAX+1);
return (int)(rnd*(Max-Min+1))+Min;
}
int main()
{
int j,counter,temp,deck[13];
srand(time(NULL));
int i;
counter=1;
for (i=0;i<12;i++)
{deck[i]=-1;
temp = getRandomNumber(0,81);
for (j=0;j<=i;j++)
{if (temp==deck[j])
{counter=0;}
if (counter!=0)
deck[i]=temp;
}
}
for(i=0;i<12;i++)
printf("%d ",deck[i]);
}
Your code has one of the weirder indentation and brace layout schemes I've ever seen:
#include <stdio.h>
#include <stdlib.h>
int getRandomNumber(int Min, int Max)
{
double rnd= (double)rand()/((double)RAND_MAX+1);
return (int)(rnd*(Max-Min+1))+Min;
}
int main()
{
int j,counter,temp,deck[13];
srand(time(NULL));
int i;
counter=1;
for (i=0;i<12;i++)
{deck[i]=-1;
temp = getRandomNumber(0,81);
for (j=0;j<=i;j++)
{if (temp==deck[j])
{counter=0;}
if (counter!=0)
deck[i]=temp;
}
}
for(i=0;i<12;i++)
printf("%d ",deck[i]);
}
Converted to a more orthodox style (Allman, more or less — see Wikipedia on Indent style), you get:
#include <stdio.h>
#include <stdlib.h>
static int getRandomNumber(int Min, int Max)
{
double rnd = (double)rand() / ((double)RAND_MAX + 1);
return (int)(rnd * (Max - Min + 1)) + Min;
}
int main(void)
{
int j, counter, temp, deck[13];
srand(time(NULL));
int i;
counter = 1;
for (i = 0; i < 12; i++)
{
deck[i] = -1;
temp = getRandomNumber(0, 81);
for (j = 0; j <= i; j++)
{
if (temp == deck[j])
{
counter = 0;
}
if (counter != 0)
deck[i] = temp;
}
}
for (i = 0; i < 12; i++)
printf("%d ", deck[i]);
}
The static and int main(void) are needed to get the code past my default compilation options; otherwise, they're cosmetic.
Now we can see some problems. The counter is set to 1 once, outside the outer loop; it is set to 0 sometimes inside the loop, but once that happens, it is never reset to 1, so no further numbers are added to the deck. You need to rework the inner loop, maybe like this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static int getRandomNumber(int Min, int Max)
{
double rnd = (double)rand() / ((double)RAND_MAX + 1);
return (int)(rnd * (Max - Min + 1)) + Min;
}
int main(void)
{
int deck[13];
srand(time(NULL));
for (int i = 0; i < 12; i++)
{
int temp = getRandomNumber(0, 81);
deck[i] = -1;
int counter = 1;
for (int j = 0; j <= i; j++)
{
if (temp == deck[j])
{
counter = 0;
break;
}
}
if (counter != 0)
deck[i] = temp;
else
i--; // Try again with a new random choice for the same i
}
const char *pad = "";
for (int i = 0; i < 12; i++)
{
printf("%s%d", pad, deck[i]);
pad = " ";
}
putchar('\n');
return 0;
}
I dislike trailing blanks, so the printing loop takes care to ensure there aren't any.
Sample output with getRandomNumber() — on macOS Sierra 10.12.2 with GCC 6.3.0:
7 73 38 61 11 13 41 66 29 39 72 20
7 2 18 17 54 31 45 40 34 22 63 16
7 13 80 54 16 49 14 58 28 53 23 26
7 24 60 10 67 53 69 32 23 2 66 12
7 34 40 48 21 3 57 43 6 18 27 80
7 45 20 3 65 21 61 17 12 69 66 27
7 67 62 78 70 57 68 46 9 2 72 39
7 77 41 34 32 75 72 20 64 78 33 25
7 6 21 72 76 11 75 38 73 27 64 33
7 17 1 27 37 28 80 49 12 67 59 36
That first number isn't very random — shortly after that sequence of tests, it changed from 7 to 8, but was equally non-random. An alternative
way of generating random numbers is:
static int getRandomNumber(int Min, int Max)
{
int rnd;
int range = (Max - Min + 1);
while ((rnd = rand()) > RAND_MAX - (RAND_MAX % range))
;
return (rnd % range) + Min;
}
This avoids the bias from that fact that 82 doesn't divide RAND_MAX exactly, which would weight the distribution of the lower numbers slightly higher than the upper numbers in the range 0..81. It also avoids the unexpectedly consistent first number, though the new first number is also semi-predictable when the tests are run at 1-second intervals.
Sample results:
48 33 28 78 14 2 81 13 23 75 38 40
45 42 74 1 11 68 17 33 78 49 23 80
42 51 38 3 5 52 35 56 54 23 59 41
39 60 2 8 36 53 79 30 72 75 62 37
36 69 45 10 78 20 71 17 6 53 54 30
33 78 9 15 75 7 40 61 27 36 70 68
30 5 55 17 69 73 25 63 37 1 21 71
27 14 19 66 57 43 1 13 3 65 71 21
24 26 62 63 41 61 68 28 67 20 74 17
21 35 26 57 28 79 47 44 2 52 60 77
Notice that the first number decreases by 3; the second seems to increase by 9; ugh — the randomness isn't all that good. It's well known that rand() is often not a high quality PRNG (pseudo-random number generator), but I'm a little surprised by this apparently systematic behaviour with seeds that differ by 1 each time.
On my Mac, when I changed srand() to srandom() and rand() to random(), I got better (as in, more unpredictable) results:
29 1 7 11 25 52 63 15 26 55 75 64
40 4 64 18 8 57 73 27 38 15 60 28
43 3 27 17 1 58 26 72 73 18 20 7
76 16 27 43 64 20 63 30 35 17 33 57
79 47 32 33 6 30 35 7 38 55 25 61
69 57 79 75 15 54 5 35 21 46 65 61
30 79 66 14 56 39 19 8 50 47 76 33
62 65 81 44 52 39 25 30 54 12 24 68
27 49 60 72 53 35 14 41 63 46 45 65
67 39 9 11 60 19 64 73 43 17 21 26
And the Mac man page for random() still suggests using arc4random() instead, but this is a lot better than plain rand(). What you find on other systems will depend on the facilities provided by the system — rand() may not be as awful as it seems to be on Mac. Basically, be cautious with your choice of PRNG — especially if you're going to use systematically generated seeds (such as the current time).
For the purpose you propose (generating a random sequence of numbers ranging from 0 through 81, where each item differs from the others), you will need a particular kind of random number generator (RNG), one that can generate all possible permutations of 82 items (expressed as 82!, or 82 factorial). However, only a limited selection of RNGs can do this. In particular the C rand() function's implementation is unspecified, so is not guaranteed to generate that many permutations.
A pseudorandom number generator (PRNG, a kind of RNG as used here) can't generate more random number sequences than its period. For 82! permutations, no PRNG with a period less than 82! can do this (the next highest power of 2 is 2408, meaning the generator needs to takes a seed at least 408 bits, or 51 bytes, long for it to possibly do so -- and 51 bytes is much bigger than srand can usually take). Alternatively, for this purpose, you would be well advised to use an RNG that generates "unpredictable" numbers, which neither the C language nor the C library includes as standard. See "Shuffling" and "Unpredictable RNGs" in my article on randomness for more information.

Resources