Reputation: 365
I am a beginner and I've been trying to run a program that prints all the numbers from 1 to N (user input) except for those that are divisible by 3 and 7 at the same time. What my code does instead, however, is that it prints the numbers from 1 to N except for those that are divisible by 3 or 7. I examined it for a while and I have no idea why it does that. Please explain to me where I'm going wrong.
static void Main(string[] args)
{
int n = 0;
int a = 0;
n = Convert.ToInt32(Console.ReadLine());
while (a <= n)
{
a++;
if (a % 3 != 0 && a % 7 != 0)
{
Console.WriteLine(a);
}
}
Console.ReadKey();
}
When I reverse the signs of the if statement to ==
the &&
operator works properly, but if the sign is !=
it simply acts like an ||
operator, so that confuses me even more. The issue is most likely in the condition, but I can't see what is wrong with it.
Upvotes: 28
Views: 5184
Reputation: 14379
Looking at your conditional statement's Truth Table you can see that if
X(NOT multiple of 3) Y(NOT multiple of 7) X && Y
true true 'a' printed as it is not a multiple of either
true false 'a' not printed, it is multiple of 7
false true 'a' not printed, it is multiple of 3
false false 'a' not printed, it is multiple of both
That is why all the multiples of 3 or 7 or 21 are not printed.
What you want: Numbers, that are
Upvotes: 5
Reputation: 39354
If you don't know how to implement an algorithm, try breaking it down into obviously correct functions that each implement part of the algorithm.
You want to "print all the numbers from 1 to N (user input) except for those that are divisible by 3 and 7 at the same time." Old timers can quickly spit out a correct and efficient implementation using logical operators. As a beginner, you may find it helps to break it down into pieces.
// write out the highest level problem to solve, using functions as
// placeholders for part of the algorithm you don't immediately know
// how to solve
for ($x = 1; $x <= $N; $x++) {
if (is_not_divisible_by_3_and_7($x)) {
print "$x\n";
}
}
// then think about the function placeholders, writing them out using
// (again) function placeholders for things you don't immediately know
// how to do
function is_not_divisible_by_3_and_7($number) {
if (is_divisible_by_3_and_7($number)) {
return false;
} else {
return true;
}
}
// keep repeating this...
function is_divisible_by_3_and_7($number) {
if (is_divisible_by_3($number) && is_divisible_by_7($number)) {
return true;
} else {
return false;
}
}
// until you have the simplest possible functions
function is_divisible_by_3($number) {
if ($number % 3 === 0) {
return true;
} else {
return false;
}
}
function is_divisible_by_7($number) {
if ($number % 7 === 0) {
return true;
} else {
return false;
}
}
This is easier to follow, because each function does one thing and the function name describes exactly that one thing. This also satisfies the first rule of programming: correct code comes first.
You can then start to think of making the code better, where better can mean:
Taking this approach with the code above, an obvious improvement is to replace is_divisible_by_3
and is_divisible_by_7
with a generic function:
function is_divisible_by_n($number, $divisor) {
if ($number % $divisor === 0) {
return true;
} else {
return false;
}
}
You can then replace all the big, bulky if x return true else return false
with the ternary operator, which gets you to:
function is_divisible_by_n($number, $divisor) {
return ($number % $divisor === 0) ? true : false;
}
function is_divisible_by_3_and_7($number) {
return (is_divisible_by_n($number, 3) && is_divisible_by_n($number, 7)) ? true : false;
}
function is_not_divisible_by_3_and_7($number) {
return (is_divisible_by_3_and_7($number)) ? false : true;
}
Now, notice that is_not_divisible_by_3_and_7
looks exactly like is_divisible_by_3_and_7
, except the returns are switched, so you can collapse those into one method:
function is_not_divisible_by_3_and_7($number) {
// look how it changed here ----------------------------------------------VVVVV - VVVV
return (is_divisible_by_n($number, 3) && is_divisible_by_n($number, 7)) ? false : true;
}
Now, rather than using ternary operators you can leverage the fact that comparisons themselves return a value:
function is_divisible_by_n($number, $divisor) {
// this expression returns a "truthy" value: true or false
// vvvvvvvvvvvvvvvvvvvvvvvvvv
return ($number % $divisor === 0);
}
function is_not_divisible_by_3_and_7($number) {
// also returns a truthy value, but inverted because of the !
// vvv
return ! (is_divisible_by_n($number, 3) && is_divisible_by_n($number, 7));
}
Finally, you can just mechanically replace the function calls with their equivalent logical operations:
for ($x = 1; $x <= $N; $x++) {
// all I did below was copy from the function, replace variable names
// v vvvvvvvvvvvvvv vvvvvvvvvvvvvv
if (! (($x % 3 === 0) && ($x % 7 === 0))) {
print "$x\n";
}
}
As bonus points, you can then apply DeMorgan's rule, to distribute the not through the expression:
for ($x = 1; $x <= $N; $x++) {
if ($x % 3 !== 0 || $x % 7 !== 0) {
print "$x\n";
}
}
Additionally, you might observe that two co-prime numbers have common factors if and only if they have common factor N times M, so:
for ($x = 1; $x <= $N; $x++) {
if ($x % (3*7) !== 0) {
print "$x\n";
}
}
You can take this further by using your language's features to compact the expression more:
array_walk(
range(1, $N),
function ($x) {
if ($x % 21 !== 0) print "$x\n";
}
);
And so on. The point is that you start by making your code correct, then you make it better. Sometimes making code correct means thinking long and hard. Sometimes it just means writing it out in very small, very explicit steps.
Upvotes: 1
Reputation: 941297
What you said:
if not (divisible by 3 and divisible by 7) then print
What you wrote:
if not divisible by 3 and not divisible by 7 then print
Not the same thing. Aristotle thought of it first, Augustus De Morgan wrote the laws 158 years ago, apply the not operator to the operands and invert the logical operation:
if not divisible by 3 or not divisible by 7 then print
Which produces:
if (a % 3 != 0 || a % 7 != 0)
Or just write it the way you said it:
if (!(a % 3 == 0 && a % 7 == 0))
Upvotes: 9
Reputation: 1
Obviously && and || are different.
It states: if (true && false) = false if (true || false) = true
Upvotes: -1
Reputation: 6413
Should be:
if ( !(a % 3 == 0 && a % 7 == 0) )
{
Console.WriteLine(a);
}
It means exactly: all the numbers except for those that are divisible by 3 and 7 at the same time.
You could also rephrase it as:
if ( a % 3 != 0 || a % 7 != 0 )
{
Console.WriteLine(a);
}
Upvotes: 9
Reputation: 20754
You should read De Morgan's laws
"not (A and B)" is the same as "(not A) or (not B)"
also,
"not (A or B)" is the same as "(not A) and (not B)".
a % 3 != 0 && a % 7 != 0
is true when a
is not divisible by 3 (a % 3 != 0
) and not divisible by 7 (a % 7 != 0
). So all a
s which are divisible by 3 or 7 (3,6,7,9,12,14,...)
makes the whole expression false. You can rephrase it like !(a % 3 == 0 || a % 7 == 0)
Upvotes: 73
Reputation: 109547
"Except numbers that are divisible by 3 and 7 at the same time" can be broken down as follows:
"divisible by 3 and 7 at the same time"
can be expressed as:
"(divisible by 3 and divisible by 7)"
"Except"
can be expressed as "Not"
.
So you get:
Not (divisible by 3 and divisible by 7)
"divisible by 3" is (a % 3) == 0
"divisible by 7" is (a % 7) == 0
Giving:
Not ( (a % 3) == 0 and (a % 7) == 0)
In C# Not
becomes !
and and
becomes &&
, so you can write the whole thing in C# as:
if (!((a % 3) == 0 && (a % 7) == 0))
Compare with your incorrect:
if (a % 3 != 0 && a % 7 != 0)
This latter is incorrect because it means:
if (the number is not divisible by 3) and (the number is not divisible by 7
).
i.e. it means "Print the number if it is neither divisible by 3 nor divisible by 7"
, which means "don't print the number if it's divisible by 3 or 7"
.
To see why, first consider the number 6:
6 is not divisible by 3? = false (because 6 *is* divisible by 3)
6 is not divisible by 7? = true (because 6 is *not* divisible by 7)
So this resolves to if false and true
which is, of course, false
.
This result also applies to any other number divisible by 3, so no numbers divisible by 3 will be printed.
Now consider the number 14:
14 is not divisible by 3? = true (because 14 is *not* divisible by 3)
14 is not divisible by 7? = false (because 14 *is* divisible by 7)
So this resolves to if true and false
which is, of course, false
.
This result also applies to any other number divisible by 7, so no numbers divisible by 7 will be printed.
Hopefully you can see why it's wrong now. If not, consider this equivalent example:
Suppose we have four people, Tom the Carpenter, Dick the Carpenter, Harry the Butcher and Tom the Butcher.
This question is equivalent to the one you're asking:
Name every person who is (not called Tom and is not a Butcher)
And you should be able to see that this the same as asking:
Name every person except (anyone called Tom or anyone who is a Butcher)
In both cases, the answer is Dick the Carpenter.
The question you should have asked is:
Name every person except (anyone called Tom who is also a butcher)
To which the answer is Tom the Carpenter, Dick the Carpenter and Harry the Butcher.
Footnote: De Morgan's laws
The second law states that:
"not (A or B)" is the same as "(not A) and (not B)"
This is the equivalent of my example above where:
Name every person except (anyone called Tom or anyone who is a Butcher)
is the equivalent to:
Name every person who is (not called Tom and is not a Butcher)
where A is anyone called Tom
and B is anyone who is a butcher
and not
is written as except
.
Upvotes: 95
Reputation: 14379
All you really need is:
if ((a%21) != 0) Console.WriteLine(a);
Explanation: The numbers that are divisible by both a and b are essentially the numbers divisible by the LCM of a and b. Since, 3 and 7 are prime number, you are basically looking for numbers that are not divisible by 3*7.
Upvotes: 9
Reputation: 55589
a % b != 0
means "a is not divisible by b".
If something is not divisible by 3 and not divisible by 7, it's divisible by neither. Thus if it's a multiple of 3 or a multiple of 7, your statement will be false.
It often helps to think of logic in terms of real-world things:
(keep in mind that true and false == false
and true or false == true
)
The ocean is blue (a is divisible by 3).
The ocean is not yellow (a is not divisible by 7).What you have:
The ocean is not blue and the ocean is not yellow - this is false (you want this to be true).What you want:
The ocean is not (blue and yellow) - this is true (the ocean is only blue, not both blue and yellow).
The ocean is not blue or the ocean is not yellow - this is true (the ocean is not yellow).
The equivalent of the last 2 statements would be:
!(a % 3 == 0 && a % 7 == 0)
(a % 3 != 0 || a % 7 != 0)
And you can convert one to the other using De Morgan's laws.
Upvotes: 4
Reputation:
&& behaves differently to ||
To understand the difference, it may help to do some tests with simpler expressions:
if (true && false)
if (true || false)
So, your problem is with understanding the other operators in your code (!= and %).
It often helps to split conditions into smaller expressions, with explanations:
bool divisbleBy3 = (a % 3 == 0);
bool divisbleBy7 = (a % 7 == 0);
if (divisbleBy3 && divisibleBy7)
{
// do not print
}
else
{
// print
}
Upvotes: 0