I have written the code for a rail fence cipher decoder, the input provided is the number of rails and the already encoded text. The decoder works, however, it only works for small inputs or short words. If i try entering a paragraph with a rails of 10 no output shows up.
I am still trying to grasp the concept of Order of a function, so I'm unsure of the order of my program. How do I make it more efficient or improve its efficiency?
Decrypt Function:
void decrypt(int rail,char *cipher) {
int length = strlen(cipher);
int i, j;
int counter=0,num=0,in=1;
int railfence[rail][100],count[100];
for(i=0;i < length;i++)
count[i]=0;
for(i=0;i < length;i++){
if(num + in == rail) in =- 1;
else if(num + in == -1)in = 1;
railfence[num][count[num]] = i;
++count[num];
num += in;
}
char buffer[1000];
for(i=0;i < rail;i++)
for(j=0;j < count[i];j++){
buffer[railfence[i][j]] = cipher[counter];
++counter;
}
buffer[length]='\0';
printf("%s\n",buffer);
}
Any help would be appreciated.
When your rail is larger than the message length, your have uninitialised string lengths for each rail. You initialise count in a loop from 0 to the message length, but you should initalise a length for each rail:
for (i = 0; i < rail; i++)
count[i] = 0;
That change should fix your error.
You are using variable-length arrays, and you know the size of these arrays. So there's no need to guess the dimensions (and make them large enough just in case). You can determine the lengths:
int zigzag[rail][length / (rail - 1) + 1];
int count[rail];
char buffer[length + 1];
(The zigzag array's inner rails have twice the number of letters in the two outer rails. Considering top and bottom rail as one, each rail must cater for at most length / (rail - 1) + 1. The + 1 at the end catches division cutoffs.)
The algorithm can probably be made more efficient by walking the message without an additional two-dimensional array. Traverse rail i with an alternating stride of 2 * (rail - 1 - i) and 2 * i. You must take care not to treat the letters of the top and bottom rails twice (when the stride is zero).
This should be considered a teaser for an optimal implementation:
if (rail == 0 || rail == railCount - 1) {
plaintextOffset = rail + offInRail * (iterfreq * 2);
} else {
if (offInRail % 2 == 0) {
plaintextOffset = rail + offInRail * iterfreq;
} else {
plaintextOffset = railCount - rail - 1 + offInRail * iterfreq;
}
}
You'll need to iterate over the rails, keeping the starting offset of the rail in the ciphertext in mind. Then you can iterate over the possible characters in the rail, while testing if plaintextOffset is not too high.
Example output using the Wikipedia plaintext as test vector:
Rails: 3, size 25
CT rail 0 : 0 + 0 = 0 <-> 0 PT
CT rail 0 : 0 + 1 = 1 <-> 4 PT
CT rail 0 : 0 + 2 = 2 <-> 8 PT
CT rail 0 : 0 + 3 = 3 <-> 12 PT
CT rail 0 : 0 + 4 = 4 <-> 16 PT
CT rail 0 : 0 + 5 = 5 <-> 20 PT
CT rail 0 : 0 + 6 = 6 <-> 24 PT
CT rail 0 : 7 + 7 = 14 <-> 28 PT (too high)
CT rail 1 : 7 + 0 = 7 <-> 1 PT
CT rail 1 : 7 + 1 = 8 <-> 3 PT
CT rail 1 : 7 + 2 = 9 <-> 5 PT
CT rail 1 : 7 + 3 = 10 <-> 7 PT
CT rail 1 : 7 + 4 = 11 <-> 9 PT
CT rail 1 : 7 + 5 = 12 <-> 11 PT
CT rail 1 : 7 + 6 = 13 <-> 13 PT
CT rail 1 : 7 + 7 = 14 <-> 15 PT
CT rail 1 : 7 + 8 = 15 <-> 17 PT
CT rail 1 : 7 + 9 = 16 <-> 19 PT
CT rail 1 : 7 + 10 = 17 <-> 21 PT
CT rail 1 : 7 + 11 = 18 <-> 23 PT
CT rail 1 : 19 + 12 = 31 <-> 25 PT (too high)
CT rail 2 : 19 + 0 = 19 <-> 2 PT
CT rail 2 : 19 + 1 = 20 <-> 6 PT
CT rail 2 : 19 + 2 = 21 <-> 10 PT
CT rail 2 : 19 + 3 = 22 <-> 14 PT
CT rail 2 : 19 + 4 = 23 <-> 18 PT
CT rail 2 : 19 + 5 = 24 <-> 22 PT
CT rail 2 : 25 + 6 = 31 <-> 26 PT (too high)
WECRLTEERDSOEEFEAOCAIVDEN
Rails: 3, size 25
CT rail 0 : 0 + 0 = 0 <-> 0 PT
CT rail 0 : 0 + 1 = 1 <-> 4 PT
CT rail 0 : 0 + 2 = 2 <-> 8 PT
CT rail 0 : 0 + 3 = 3 <-> 12 PT
CT rail 0 : 0 + 4 = 4 <-> 16 PT
CT rail 0 : 0 + 5 = 5 <-> 20 PT
CT rail 0 : 0 + 6 = 6 <-> 24 PT
CT rail 0 : 7 + 7 = 14 <-> 28 PT (too high)
CT rail 1 : 7 + 0 = 7 <-> 1 PT
CT rail 1 : 7 + 1 = 8 <-> 3 PT
CT rail 1 : 7 + 2 = 9 <-> 5 PT
CT rail 1 : 7 + 3 = 10 <-> 7 PT
CT rail 1 : 7 + 4 = 11 <-> 9 PT
CT rail 1 : 7 + 5 = 12 <-> 11 PT
CT rail 1 : 7 + 6 = 13 <-> 13 PT
CT rail 1 : 7 + 7 = 14 <-> 15 PT
CT rail 1 : 7 + 8 = 15 <-> 17 PT
CT rail 1 : 7 + 9 = 16 <-> 19 PT
CT rail 1 : 7 + 10 = 17 <-> 21 PT
CT rail 1 : 7 + 11 = 18 <-> 23 PT
CT rail 1 : 19 + 12 = 31 <-> 25 PT (too high)
CT rail 2 : 19 + 0 = 19 <-> 2 PT
CT rail 2 : 19 + 1 = 20 <-> 6 PT
CT rail 2 : 19 + 2 = 21 <-> 10 PT
CT rail 2 : 19 + 3 = 22 <-> 14 PT
CT rail 2 : 19 + 4 = 23 <-> 18 PT
CT rail 2 : 19 + 5 = 24 <-> 22 PT
CT rail 2 : 25 + 6 = 31 <-> 26 PT (too high)
WEAREDISCOVEREDFLEEATONCE
I'm not super familiar with the algorithm, but it seems like you could save a bunch of time and memory if first you found the length of the string, then found the number of spaces (possibly using this), subtracted those two numbers, and then divided by rail (let's call the resulting number numCols). Then you'd just create one 2D char array, with rail rows and numCols columns. Go through with two for loops (columns nested inside of the rows) inserting the characters in order (making sure to avoid spaces).
Once you've done that, just read them back out, except this time, nest your rows loop inside your columns loop.
Related
I am trying to learn awk by solving code puzzles. I am trying to read several "grids" of integers (representing bingo boards as per https://adventofcode.com/2021/day/4) into a three-dimensional awk array. An example "grid" can look like this:
22 13 17 11 0
8 2 23 4 24
21 9 14 16 7
6 10 3 18 5
1 12 20 15 19
And there are several of these in a longer input file. After reading each line into an array rows I am attempting to organize the numbers into this multi dimensional array called boards. Here is my example code:
{
b = 0
for (i in rows) {
split(rows[i], nums, " ")
for (j in nums) {
r = i % 5
n = j - 1
boards[b][r][n] = nums[j]
print b, r, n, nums[j], boards[b][r][n]
}
if (i%5==0)
++b
}
print boards[0][1][1]
}
Notice the debug printout print b, r, n, nums[j], boards[b][r][n] which indeed outputs the correct values for boards[b][r][n] on that row:
0 0 0 22 22
0 0 1 13 13
0 0 2 17 17
Etc. This seems to verify that the multi dimensional array gets written properly. Yet on the final line of the example code, the output is instead empty. I have tried using the form boards[b, r, n] for the array as well with the exact same result. Obviously there's something I'm not quite understanding here. Any help is appreciated. Full code for reproducibility:
# === ex.txt ===
7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1
22 13 17 11 0
8 2 23 4 24
21 9 14 16 7
6 10 3 18 5
1 12 20 15 19
3 15 0 2 22
9 18 13 17 5
19 8 7 25 23
20 11 10 24 4
14 21 16 12 6
14 21 17 24 4
10 16 15 9 19
18 8 23 26 20
22 11 13 6 5
2 0 12 3 7
# === solve.awk ===
BEGIN {
r = 0
}
{
if (NR == 1)
split($0, draws, "")
else if (NR != 2 && (NR-3)%6 != 5)
rows[r++] = $0
}
END {
b = 0
for (i in rows) {
split(rows[i], nums, " ")
for (j in nums) {
r = i % 5
n = j - 1
boards[b][r][n] = nums[j]
print b, r, n, nums[j], boards[b][r][n]
}
if (i%5==0)
++b
}
print boards[0][1][1]
}
I run this with awk -f solve.awk ex.txt. awk --version outputs GNU Awk 5.1.1, API: 3.1 (GNU MPFR 4.1.0-p13, GNU MP 6.2.1) as its first line. Thank you!
You are incrementing b at the end of the first iteration of your for (i in rows) loop because i == 0 ==> i%5 == 0, while you want to do it at the end of the 5th iteration. Try if (i%5 == 4) ++b.
Note that as you use GNU awk you could simplify all this. When the record separator (RS) is the empty string the records are separated by empty lines (one record per board):
$ awk -v RS='' '
NR>1 {
a[NR-2][1]; split($0, a[NR-2]);
}
END {
for(b in a) for(r in a[b])
boards[b][int((r-1)/5)][(r-1)%5] = a[b][r];
for(b in boards) for(r in boards[b]) for(n in boards[b][r])
print b, r, n, boards[b][r][n]
}' ex.txt
0 0 0 22
0 0 1 13
0 0 2 17
0 0 3 11
0 0 4 0
0 1 0 8
...
I have managed to transfer the 2d array int a 1d array to make it simpler. So given:
local table = {0,0,0,0,0,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,0,30,31,32,33, 0,34,35,36,37, 0 }
How can I extract a sub-array as below?
local sub = {2,3,4,7,8,9,12,13,14,17,18,19,22,23,24}
Here's one way...
> t = {}
> for i = 7, 30, 5 do
>> for j = i, i + 2 do
>> t[#t+1] = ble[j]
>> end
>> end
> for k,v in ipairs(t) do print(k,v) end
1 2
2 3
3 4
4 7
5 8
6 9
7 12
8 13
9 14
10 17
11 18
12 19
13 22
14 23
15 24
>
I renamed your table to ble so it doesn't shadow the table library.
I have an array:
v = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20];
I want to extract multiple arrays of length 3 equally spaced by 6 elements, starting at the fifth element of v, and then combine them together:
v1 = [5 6 7];
v2 = [11 12 13];
v3 = [17 18 19];
v_combined = [5 6 7 11 12 13 17 18 19];
Are there any simple ways to do this without using a for loop?
You can do it using logical indexing. You need to create an index mask like this
idx = [0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0]
which you can create like this:
idx = false(size(v))
k = 5
idx(k:end) = ~mod(floor((0:numel(v)-k)/3),2)
And finally
v_combined = v(idx)
In general for m elements spaces by n elements starting from k you can use
k=5;
m=3;
n=6;
I=1:numel(v);
v_combined = v((I>=k) & mod(I-k,n)<m)
To clarify the confusion - I want to write a function that maps a number to the following list of letter combinations.
My question is best illustrated with the following table.
A 1
B 2
C 3
AA 4
AB 5
AC 6
BA 7
BB 8
BC 9
CA 10
CB 11
CC 12
AAA 13
AAB 14
AAC 15
ABA 16
ABB 17
ABC 18
ACA 19
ACB 20
ACC 21
BAA 22
BAB 23
BAC 24
BBA 25
BBB 26
BBC 27
I want to design a function that is able to map a given number, to the left column of this here table. I've tried assigning numerals to the letters first.
A = 0
B = 1
C = 2
This allows me form the following table (Cn - Column number, from right to left).
C3 C2 C1 Number
0 1
1 2
2 3
0 0 4
0 1 5
0 2 6
1 0 7
1 1 8
1 2 9
2 0 10
2 1 11
2 2 12
0 0 0 13
0 0 1 14
0 0 2 15
0 1 0 16
0 1 1 17
0 1 2 18
0 2 0 19
0 2 1 20
0 2 2 21
1 0 0 22
1 0 1 23
1 0 2 24
1 1 0 25
1 1 1 26
1 1 2 27
So this looks like an recursive loop type algorithm, but I can't figure out how to put this down in code. Any suggestions?
As whoever the person was (user: n.m.) who wrote the comment that disappeared, this is just base-3 counting, except all numerals are offset by +1. The digits really stand for A=0, B=1, C=2
Hence BBC = ('B'+1)*3^2 + ('B'+1)*3 + ('C'+1) = 2*9 + 2*3 + 3 = 27
The pseudocode for fromInt(), Antoine has already given you it. Same idea:
char* fromInt(int n) {
result = ""
working_val = (n-1)
while (working_val>0) {
Prepend to result the digit "CAB"[ working_val % 3 ]
working_val /= 3
}
return result
}
Strictly we don't care about catching the special-case 0 which Antoine noted, because your list doesn't have a representation for 0.
#include <stdio.h>
#include <string.h>
int toInt(const char *str, int acc) {
if(*str)
return toInt(++str, acc * 3 + *str - 'A' + 1);
else
return acc;
}
char *fromInt(int n, char *outbuff){
char* p = outbuff;
while(n){
*p++ = 'A' + ((n % 3 == 0)? 3 : n % 3) - 1;
n = (n - 1) / 3;
}
*p = '\0';
return strrev(outbuff);//strrev isn't ANSI C
}
int main(void) {
char buff[] = "BBC";
int x = toInt(buff, 0);
printf("%d\n", x);
printf("%s\n", fromInt(x, buff));
return 0;
}
This is a kind of base-3 system, but the digits are 1 (A), 2 (B) and 3 (C), there is no 0.
The conversion formula from this representation is, as usual,
3^n*a_n + 3^(n-1)*a_{n-1} + ... + 3^0*a_0
The reverse conversion is just like a regular conversion to base 3, the only difference is that a modified remainder function is used:
int modified_remainder(m, n)
{
int answer = m % n;
if (answer == 0) answer = n;
return answer;
}
Now given the number m, the last digit of its representation would be
a_0 = modified_remainder(m, 3)
The one before last is
m_1 = (m - a_0) / 3; // m-a_0 is always divisible by 3
a_1 = modified_remainder(m_1, 3)
The next one is
m_2 = (m_1 - a_1) / 3
a_2 = modified_remainder(m_2, 3)
and so on. You stop when m_k < n.
Try to verify these claims, it's a good exercise.
Given,
a = [2 4 6 8 10 0 7 18 9 0 8 2 0 5];
b = [1 3 0 5 70 8 6 87 1 9 7 8 0 2];
I am trying to delete elements (in both 'a' & 'b') that corresponds
to '0' or less than '0' in either 'a' or 'b' i.e., I want
% a = [2 4 8 10 7 18 9 8 2 5];
% b = [1 3 5 70 6 87 1 7 8 2];
I am trying like this -
n = length(b);
a1 = [];
b1 = [];
for k = 1:n
if a(n) <= 0 || b(n) <= 0
a1 = [a; a(a > 0)] % eliminates 0 from a
b1 = [b; b(b > 0)] % eliminates 0 from b
end
end
Any help will be very helpful.
Use find:
a = [2 4 6 8 10 0 7 18 9 0 8 2 0 5];
b = [1 3 0 5 70 8 6 87 1 9 7 8 0 2];
A = a( find( a > 0 & b > 0 ) );
B = b( find( a > 0 & b > 0 ) );
or even faster:
C = a( a > 0 & b > 0 );
D = b( a > 0 & b > 0 );
returns:
C =
2 4 8 10 7 18 9 8 2 5
D =
1 3 5 70 6 87 1 7 8 2
If you can be sure, that there are no values below zero you could also use:
E = a( logical(a) & logical(b) );
F = b( logical(a) & logical(b) );
which is a little faster, but containing also negative values.
The efficient and compact way to do this is to first create the relevant index, that prevents double calculation:
idx = a>0 & b>0
a = a(idx);
b = b(idx);