- Binary
1111— four ones
Check Evil Number in JavaScript
What you’ll learn
- The definition of an evil number: even count of
1-bits in binary (nonnegative integers). - A bitwise popcount using
>>>after>>> 0plus a division-by-two variant for a small range. - Live preview, edge cases (
0, negatives), and links to related topics.
Overview
Evil numbers classify integers by the parity of their Hamming weight in base 2. The check is: count 1 bits, then test whether that count is divisible by 2.
Two programs
One value (15) and evils in 1–10 matching the classic output.
Live preview
Nonnegative safe integers; shows popcount and evil vs odious.
Rigor
>>> unsigned shifts after >>> 0; nonnegative definition.
Prerequisites
Loops, % and division, basic bitwise &, and >>>.
console.log,Number.isSafeInteger, and nonnegative conventions.- Comfort reading small binary expansions (e.g.
15 = 11112in prose as1111).
What is an evil number?
A nonnegative integer n is evil if the binary representation of n contains an even number of 1 digits. If the count is odd, n is odious.
This is pure bit-level parity, unrelated to divisibility by 2 in decimal. The smallest evil numbers are 0, 3, 5, 6, 9, 10, 12, 15, ….
Hamming weight
Write n = ∑ b_i 2^i with b_i ∈ {0,1}. The Hamming weight is s_2(n) = ∑ b_i. Then n is evil iff s_2(n) ≡ 0 (mod 2).
1515 = 8+4+2+1 = 11112, so s_2(15)=4 and 15 is evil.
Intuition
- Binary
111— three ones
Takeaway: flip one bit toggles parity, so consecutive numbers are not always both evil or both odious.
Live preview
Nonnegative integers in the JavaScript safe range. Popcount matches the division-by-two mental model.
Algorithm
Goal: decide whether n ≥ 0 has an even number of 1-bits.
Popcount
Peel the least significant bit with n & 1 and n >>>= 1 after n >>> 0, or emulate binary digits with n % 2 and Math.floor(n / 2) for n ≥ 0.
Parity of the count
Evil iff count % 2 === 0. Range listing applies the predicate per index.
📜 Pseudocode
function popcount(n): // assume n >= 0
c = 0
while n > 0:
c += (n mod 2)
n = floor(n / 2)
return c
function isEvil(n):
return (popcount(n) mod 2) = 0Bitwise popcount with >>>
Same verdict as the reference for 15. Coerces to unsigned 32-bit with >>> 0 before shifting so each step matches an unsigned right shift.
function countSetBitsU(n) {
let count = 0;
let x = n >>> 0;
while (x !== 0) {
count += x & 1;
x >>>= 1;
}
return count;
}
function isEvilU(n) {
return countSetBitsU(n) % 2 === 0;
}
const number = 15;
if (number < 0) {
console.log("Evil/odious here are defined for nonnegative n only.");
} else if (!Number.isSafeInteger(number)) {
console.log("Use a JavaScript safe integer.");
} else if (isEvilU(number)) {
console.log(number + " is an Evil Number.");
} else {
console.log(number + " is not an Evil Number.");
}Explanation
countSetBitsU clears bits from the right until the value is zero. 15 has four 1 bits, so the count is even. Values larger than 232-1 truncate modulo 232 when using >>> 0; use the division loop if you need full safe integers.
Evil numbers in [1, 10]
Matches the reference output: 3 5 6 9 10. Uses the division-and-modulo loop (equivalent for nonnegative n).
function isEvilNonNeg(num) {
let ones = 0;
while (num > 0) {
if (num % 2 === 1) {
ones++;
}
num = Math.floor(num / 2);
}
return ones % 2 === 0;
}
console.log("Evil numbers in the range 1 to 10:");
let line = "";
for (let i = 1; i <= 10; i++) {
if (isEvilNonNeg(i)) {
line += i + " ";
}
}
console.log(line.trim());Explanation
Each iteration reads the least significant binary digit of num via num % 2, then shifts right with Math.floor(num / 2).
Optimization
Kernighan trick. On unsigned 32-bit math, repeatedly set x = x & (x - 1) after x >>> 0; each step clears one 1.
Built-ins. Engines optimize bitwise ops heavily; for BigInts you still loop or use ad hoc popcount.
Interview: define the nonnegative convention, mention 0, and contrast >> vs >>>.
❓ FAQ
🔄 Input / output examples
Change number in Example 1 or extend the loop bounds in Example 2.
| n | Binary | Ones | Verdict |
|---|---|---|---|
0 | 0 | 0 | Evil |
3 | 11 | 2 | Evil |
7 | 111 | 3 | Odious |
15 | 1111 | 4 | Evil |
Edge cases and pitfalls
The mathematical definition is for nonnegative integers. Negative numbers use two's complement encodings; mixing naive >> on negatives with counting semantics needs an explicit convention.
n = 0
Popcount 0 is even, so 0 is evil.
Negative numbers
Example 1 rejects n < 0 for clarity. If you must classify negatives, map them with >>> 0 or BigInt and document which bits you count.
Leading zeros
Infinitely many leading zeros do not change the finite count of 1 bits in the minimal binary form.
1 ≤ i ≤ 10
Example 2 omits 0 by loop start; include 0 if the problem asks for nonnegative evils in the interval.
⏱️ Time and space complexity
| Operation | Time | Extra space |
|---|---|---|
Popcount of n | O(log n) bit steps | O(1) |
| Kernighan variant | O(popcount(n)) | O(1) |
Scan [a,b] | O((b-a+1) log U) | O(1) |
Here U = max(|n|, 1) for a single value, or the largest value in the scanned range.
Summary
- Definition: evil iff the binary digit sum is even; otherwise odious.
- Code: bitwise with
>>>after>>> 0, or% 2withMath.floor(n / 2)forn ≥ 0. - Watch-outs: define nonnegative scope; avoid mixing
>>sign extension when you want unsigned walks.
The complementary class is odious numbers: nonnegative integers whose binary expansion has an odd number of 1 bits. The name “evil” vs “odious” is mathematical wordplay, not moral judgment.
8 people found this page helpful
