How to exit std.algorithm.iteration.reduce prematurely? - d

I have the following function findFirstDuplicateFrequency that implements (correctly) an algorithm for a programming puzzle.
Instead of imperative looping I'd like to promote D's functional features and thought I can apply reduce to the problem.
I run into an issues that I need to be able to iterate the input sequence multiple (but unknown) times and quit the processing when the exit condition is met.
AFAICS the standard reduce can't be exited in the middle of the processing and I also struggle how to carry on extra accumulator information that is used to calculate the exit condition.
So what would be the correct (?) idiomatic D approach to solve the problem in a (more) functional way ?
This is my very first D program ever and so all other comments are welcome too !
import std.conv: to;
/**
From: https://adventofcode.com/2018/day/1
You notice that the device repeats the same frequency change list over and
over. To calibrate the device, you need to find the first frequency it reaches
twice.
For example, using the same list of changes above, the device would loop as
follows:
Current frequency 0, change of +1; resulting frequency 1.
Current frequency 1, change of -2; resulting frequency -1.
Current frequency -1, change of +3; resulting frequency 2.
Current frequency 2, change of +1; resulting frequency 3.
(At this point, the device continues from the start of the list.)
Current frequency 3, change of +1; resulting frequency 4.
Current frequency 4, change of -2; resulting frequency 2, which has already been seen.
In this example, the first frequency reached twice is 2. Note that your device
might need to repeat its list of frequency changes many times before a
duplicate frequency is found, and that duplicates might be found while in the
middle of processing the list.
Here are other examples:
+1, -1 first reaches 0 twice.
+3, +3, +4, -2, -4 first reaches 10 twice.
-6, +3, +8, +5, -6 first reaches 5 twice.
+7, +7, -2, -7, -4 first reaches 14 twice.
What is the first frequency your device reaches twice?
*/
int findFirstDuplicateFrequency(int[] frequencyChanges)
pure
{
int[int] alreadySeen = [0:1];
int frequency = 0;
out_: while(true) {
foreach(change; frequencyChanges) {
frequency += change;
if (int* _ = frequency in alreadySeen) {
break out_;
} else {
alreadySeen[frequency] = 1;
}
}
}
return frequency;
} unittest {
int answer = 0;
answer = findFirstDuplicateFrequency([1, -2, 3, 1]);
assert(answer == 2, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequency([1, -1]);
assert(answer == 0, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequency([3, 3, 4, -2, -4]);
assert(answer == 10, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequency([-6, 3, 8, 5, -6]);
assert(answer == 5, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequency([7, 7, -2, -7, -4]);
assert(answer == 14, "Got: " ~ to!string(answer));
}
void main() {}

Even according to #AdamD.Ruppe there is no great hopes to make the code more "functional" I was inspired by #BioTronic's cumulativeFold + until hint and decided to have a second look.
Unfortunately I see no way to apply cumulativeFold + until as I don't have a sentinel value required by until and there is still the same stopping problem than with reduce.
When I was browsing standard runtime library reference I noticed each do have an early exit (aka partial iteration) option. I also run into std.range.cycle.
Combining these two shiny new things I came to the solution below. findFirstDuplicateFrequencyV2 uses cycle and each to replace the imperative loops of the first version. I'm not sure if this version is any simpler but hopefully it is more "trendy" !
int findFirstDuplicateFrequencyV2(int[] frequencyChanges)
pure
{
import std.algorithm.iteration : each;
import std.range : cycle;
import std.typecons : Yes, No;
int[int] alreadySeen = [0:1];
int frequency = 0;
frequencyChanges.cycle.each!((change) {
frequency += change;
if (frequency in alreadySeen) {
return No.each;
}
alreadySeen[frequency] = 1;
return Yes.each;
});
return frequency;
} unittest {
import std.conv : to;
int answer = 0;
answer = findFirstDuplicateFrequencyV2([1, -2, 3, 1]);
assert(answer == 2, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequencyV2([1, -1]);
assert(answer == 0, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequencyV2([3, 3, 4, -2, -4]);
assert(answer == 10, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequencyV2([-6, 3, 8, 5, -6]);
assert(answer == 5, "Got: " ~ to!string(answer));
answer = findFirstDuplicateFrequencyV2([7, 7, -2, -7, -4]);
assert(answer == 14, "Got: " ~ to!string(answer));
}

Related

Minimum Deletions to Make String Balanced

Minimum Deletions to Make String Balanced - LeetCode
1653. Minimum Deletions to Make String Balanced
Medium
You are given a string s consisting only of characters 'a' and 'b'​​​​.
You can delete any number of characters in s to make s balanced. s is balanced if there is no pair of indices (i,j) such that i < j and s[i] = 'b' and s[j]= 'a'.
Return the minimum number of deletions needed to make s balanced.
Example 1:
Input: s = "aababbab"
Output: 2
Explanation: You can either:
Delete the characters at 0-indexed positions 2 and 6 ("aababbab" -> "aaabbb"), or
Delete the characters at 0-indexed positions 3 and 6 ("aababbab" -> "aabbbb").
Example 2:
Input: s = "bbaaaaabb"
Output: 2
Explanation: The only solution is to delete the first two characters.
Constraints:
1 <= s.length <= 105
s[i] is 'a' or 'b'​​.
class Solution {
public:
int minimumDeletions(string s) {
int cnt=0;
stack<int> st;
st.push(s[0]);
for(int i=1;i<s.length();i++){
if(s[i]=='b'){
st.push(s[i]);
}
else{
if(st.empty()==true){
st.push('a');
}
else if(st.top()=='a'){
st.push('a');
}
else{
while(st.empty()==false and st.top()=='b'){
cnt++;
st.pop();
}
st.push('a');
}
}
}
return cnt;
}
};
for both the exapmles the answer should be 2,2
but my code it giving 3,2
I recommend to learn some test framework. IMO two best are:
gtest - industry standard - old but quite powerful
catch2 - nice and more user friendly, quickly gains popularity.
Write test code which will validate your solution then use it with a debugger to find issue in your code.
Here is an example with catch2:
https://godbolt.org/z/xevvPrEeK
TEST_CASE("Sum") {
auto [result, s] = GENERATE(table<int, std::string>({
{ 0, ""},
{ 0, "a"},
{ 0, "b"},
{ 0, "ab"},
{ 1, "ba"},
{ 1, "bba"},
{ 1, "baa"},
{ 1, "aba"},
{ 2, "aababbab" },
{ 2, "bbaaaaabb" },
}));
INFO("s = " << s);
REQUIRE(result == Solution{}.minimumDeletions(s));
}
Note this test finds simplest case where your code fails.
On problem: use of the stack is obsolete. Just try find place where you have to delete all bs before it and all as after it.

Two largest contiguous subarray

I'm currently doing a problem that's similar to the maximum contiguous sub-array problem. However, instead of finding just one contiguous sub-array, I can find up to two non-overlapping contiguous subarrays.
For instance for the test case below, the answer is 20 since we can take everything but -20.
5 3 -20 4 8
To do this, I implemented the following code:
long long n, nums[500500], dp[500500][2][3];
long long best(int numsLeft, int beenTaking, int arrLeft) {
if (arrLeft < 0 || numsLeft < 0) return 0;
if (dp[numsLeft][beenTaking][arrLeft] != -1)
return dp[numsLeft][beenTaking][arrLeft];
if (beenTaking) {
// continue Taking
long long c1 = best(numsLeft - 1, beenTaking, arrLeft) + nums[numsLeft];
// stop Taking
long long c2 = best(numsLeft - 1, 0, arrLeft);
return dp[numsLeft][beenTaking][arrLeft] = max(c1, c2);
} else {
// continue not Taking
long long c1 = best(numsLeft - 1, beenTaking, arrLeft);
// start Taking
long long c2 = best(numsLeft - 1, 1, arrLeft - 1) + nums[numsLeft];
return dp[numsLeft][beenTaking][arrLeft] = max(c1,c2);
}
}
This is the function call:
cout << best(n - 1, 0, 2) << endl;
The dp array has been -1 filled before the function call. The nums array contain n elements and is zero-indexed.
Ideone.com link is this: http://ideone.com/P5PB7h
While my code does work for the sample test-case shown above, it fails for some other test-cases (that are not available to me). Are there any edge cases that are not being caught by my code? Where am I going wrong? Thank you for the help.
I tried coming up with a few such edge cases, but am unable to do so.
The problem seems to be on the following lines:
if (beenTaking) {
// continue Taking
long long c1 = best(numsLeft - 1, beenTaking, arrLeft) + nums[numsLeft];
...
} else {
...
}
Adding best(numsLeft - 1, 1, arrLeft) without decrementing arrLeft implies that the "best" results from the first numsLeft - 1 values in nums[] happens at the end of nums[] (at index numsLeft - 1). This may not be true.
The code will therefore likely fail when there are more than 2 positive ranges separated by negative values.
Also, the dp array should be initialized to something clearly out of range, like LLONG_MIN, rather than -1, which could be a legitimate sum.

Stack for backtracking maze

For the algorithm DFS for maze-generation:
1) Is the stack value supposed to change once I push the stack in whenever there is a path to go?
For example as below 5x5 Maze:
0, 0, 0, 0, 0
0, 1, 0, 0, S
0, 1, 1, 1, 1
0, D, 0, 0, 0
0, 0, 0, 0, 0
Let's say from source I move down one step (CurrentCell)
The stack will push in coordinates (5,2) (Current Source Location)
Then when I reaches (5,3), the currentCell will become the Source. (5,3)
Am i correct?
So when the source moves to the next left path, the stack will push in the current coordinate, which is (5,3) so on and so for until it reaches a dead end instead of the destination.
So as per above example:
The stack will first push in value of
Stack(0) (5,2)
Stack(1) (5,3)
. . . .
The question is, how am i supposed to push in the x and y? Because the current problem is, even if I push in the x and y, the x and y stays at the original value instead of constantly updating. That's the reason why my backtrack is not working, I guess.
Code:
(random == 3) //To check for bottom neighbor
{
if (y+ 2 < column) //In case the column overshots
{
{
maze[x][y+ 1].setValue(1); //New Path Set as 1
myStack.push(maze[x][y]);
y= y+ 1;
}
}
}
turns out I am correct.
maze[x][y] = myStack.top();
myStack.pop();
cout << "Array X is : " << maze[x][y].getX();
cout << "Array Y is : " << maze[x][y].getY();
All i need is to pop and check whether is the route I took backtracks back. Thanks all for the help!

Unexpected output from parallel_invoke in C++

I have put 3 tasks in parallel: print min, max, and average of two numbers. The first task prints min value twice, and I expect it's output to be contiguous.
int wmain()
{
__int64 elapsed;
elapsed = time_call([&]
{
double a = 1.0;
double b = 5.0;
parallel_invoke(
[&]{for( size_t i = 0; i < 2; ++i )
{
PrintMinValue(a, b);
}},
[&]{PrintMaxValue(a, b);},
[&]{PrintAvgValue(a, b);});
});
wcout << L"parallel time: " << elapsed << L" ms" << endl << endl;
}
I ran this program several times.
All outputs such as 5, 3, 1, 1 or 3, 1, 1, 5 are understandable.
However, some outputs such as 1, 5, 3, 1 are not obvious. It means that the first task that must print "1" (min value) twice in a contiguous block is split. Why?
every call to wcout or cout can be split at the '<<' operator, if you want to print a whole line without breaks use printf style output.
The code is executed in parallel. Why do you expect blocks to be contiguous? The whole point about parallel programming is that you give this guarantee up – otherwise it wouldn’t be parallel but sequential.
If you want to preserve the sequence of execution then you need to wrap your contiguous block in a critical section.

How to fix this to write a program that uses while loops to calculate the first n Fibonacci numbers

I am new to C++ programming and I am a bit lost. Here is what I am suppose to do and my code. Any ideas on what to do?
Write a program that uses while loops to calculate the first n Fibonacci numbers. Recall from math the following definition of the Fibonacci sequence:
The Fibonacci numbers Fn are defined as follows. F0 is 1, F1 is 1 and Fi+2 = Fi + Fi+1 for i = 0, 1, 2, ... . In other words, each number is the sum of the previous two numbers. The first few Fibonacci numbers are 1, 1, 2, 3, 5, 8, and 13.
The program should prompt the user for n (the number of Fibonacci numbers) and print the result to the screen. If the user enters an invalid value for n (n <= 0), print an error message and ask the user to re-enter n (an input validation loop for n). This MUST be a loop, not an if statement like Lab 2.
The output should be similar to the following:
Enter the number of Fibonacci numbers to compute: 3
The first 3 Fibonacci numbers are:
1 1 2
#include <iostream>
using namespace std;
int main()
{
int f0 = 0, f1 = 1,f2= 2, i = 0, n;
cout << "Enter the number of Fibonacci numbers to compute: ";
cin >> n;
if ( n <= 0)
{
cout <<"Error: Enter a positive number: ";
return 1;
}
while ( i < n){
f2 = f0 + f1;
i++;
}
cout << "The first " << n << " Fibonacci numbers are: " << endl;
cin >> n;
return 0;
}
while ( i < n){
f2 = f0 + f1;
i++;
}
See this loop, this is where the problem is, since this is homework, i'll not tell exactly what the problem is, take a pen and paper, and start executing your statements, specially this loop, you'll find your error. Just a hint, Fibonacci number is the sum of previous two fibonacci numbers.
You got the f2=f0+f1 right. However, you should note that when you increment i, then f2 becomes f1 and f1 becomes f0.
If you name them like this, it would make more sense:
int f_i_minus_2 = 0, f_i_minus_1 = 1, f_i;
and you would have
f_i = f_i_minus_1+f_i_minus_2;
Now, imagine i is 3. You have written:
f[3] = f[2]+f[1]
When you increment i, you must have:
f[4] = f[3]+f[2]
That is f_i is put in the place of f_i_minus_1 and f_i_minus_1 is put in the place of f_i_minus_2.
(Look at this:
f[3] = f[2] + f[1]
| |
\_____ \____
\ \
f[4] = f[3] + f[2]
)
So you need two assignments after computing f_i:
f_i_minus_2 = f_i_minus_1;
f_i_minus_1 = f_i;
Note that I first changed f_i_minus_2 to f_i_minus_1 because the second assignment destroys the value of f_i_minus_1.
According to wikipedia, your definition is off. F0=0, F1=1, F2=1, F3=2, ...
http://en.wikipedia.org/wiki/Fibonacci_number
Assuming wikipedia is right your loop is basically
int i = 0, f, fprev;
while( i < n )
{
if( i == 0 )
{
f = 0;
fprev = 0;
}
else if( i == 1 )
{
f = 1;
}
else
{
int fnew = f + fprev;
fprev = f;
f = fnew;
}
i++;
}
As others have pointed out, since you never modify f0 and f1 in the
loop, f2 isn't going to depend on the number of times through the
loop. Since you have to output all of the numbers at the end anyway,
why not try keeping them in an array. I'd initialize the first two
values manually, then loop until I had enough values.
(This can be done quite nicely using the STL:
// After having read n...
std::vector<int> results( 2, 1 );
while ( results.size() < n )
results.push_back( *(results.end() - 1) + *(results.end() - 2));
I'm not sure that this is what your instructor is looking for, however.
I rather suspect that he wants you to to some indexing yourself. Just
remember that if you initialize the first two values manually, your
index must start at 2, not at 0.)
Another thing: the specification you post says that you should loop if
the user enters an illegal value. This is actually a little tricky: if
the user enters something that isn't an int (say "abc"), then 1)
std::cin will remain in error state (and all further input will fail)
until cleared (by calling std::cin.clear()), and the illegal
characters will not be extracted from the stream, so your next attempt
will fail until you remove them. (I'd suggest >>ing into an
std::string for this; that will remove everything until the next white
space.) And don't ever access the variable you >>ed into until
you've checked the stream for failure—if the input fails. If the
input fails, the variable being input is not modified. If, as here, you
haven't initialized it, then anything can happen.
Finally (and I'm sure this goes beyond your assignment), you really do
need to do something to check for overflow. Beyond a certain point,
your output will become more or less random; it's better to stop and
output that you're giving up in this case.
If you are interested, there are better ways to calculate it.