Skip to content

Commit 5e20539

Browse files
committed
feat: add SociableNumber implementation
1 parent 6fbbc94 commit 5e20539

2 files changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.thealgorithms.maths;
2+
3+
/**
4+
* Sociable numbers are natural numbers that form a cyclic sequence where the
5+
* sum of proper divisors of each number equals the next number in the sequence,
6+
* with the sequence eventually returning to the starting number.
7+
* Amicable numbers are a special case of sociable numbers with a cycle length of 2.
8+
* Example: (12496, 14288, 15472, 14536, 14264) is a sociable cycle of length 5.
9+
*
10+
* @author Vraj Prajapati (@Rosander0)
11+
* @see <a href="https://en.wikipedia.org/wiki/Sociable_number">Wikipedia: Sociable Number</a>
12+
* @see AmicableNumber
13+
*/
14+
public final class SociableNumber {
15+
16+
private SociableNumber() {
17+
// Utility class
18+
}
19+
20+
/**
21+
* Calculates the sum of proper divisors of a number
22+
* (all divisors excluding the number itself).
23+
*
24+
* @param number the number to calculate proper divisors sum for
25+
* @return sum of proper divisors, or 0 if number is less than or equal to 1
26+
*/
27+
static int sumOfProperDivisors(final int number) {
28+
if (number <= 1) {
29+
return 0;
30+
}
31+
int sum = 1; // 1 is a proper divisor of every number > 1
32+
final int root = (int) Math.sqrt(number);
33+
for (int i = 2; i <= root; i++) {
34+
if (number % i == 0) {
35+
final int other = number / i;
36+
sum += i;
37+
if (other != i) {
38+
sum += other;
39+
}
40+
}
41+
}
42+
return sum;
43+
}
44+
45+
/**
46+
* Checks whether a number is part of a sociable cycle of a given length.
47+
* Starting from the given number, it follows the chain of proper divisor
48+
* sums and checks if it returns to the starting number in exactly cycleLength steps.
49+
*
50+
* @param number the starting number (must be positive)
51+
* @param cycleLength the expected cycle length (must be greater than 1)
52+
* @return true if the number is part of a sociable cycle of given length, false otherwise
53+
*/
54+
public static boolean isSociable(final int number, final int cycleLength) {
55+
if (number <= 0 || cycleLength <= 1) {
56+
return false;
57+
}
58+
int current = number;
59+
for (int i = 0; i < cycleLength; i++) {
60+
current = sumOfProperDivisors(current);
61+
if (current == number) {
62+
return i == cycleLength - 1;
63+
}
64+
}
65+
return false;
66+
}
67+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.thealgorithms.maths;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
/**
10+
* Tests for {@link SociableNumber}.
11+
*
12+
* @author Vraj Prajapati (@Rosander0)
13+
*/
14+
public class SociableNumberTest {
15+
16+
@Test
17+
public void testSumOfProperDivisorsEdgeCases() {
18+
assertEquals(0, SociableNumber.sumOfProperDivisors(0));
19+
assertEquals(0, SociableNumber.sumOfProperDivisors(-5));
20+
assertEquals(0, SociableNumber.sumOfProperDivisors(1));
21+
assertEquals(1, SociableNumber.sumOfProperDivisors(2));
22+
}
23+
24+
@Test
25+
public void testSociableCycleOfLengthFive() {
26+
assertTrue(SociableNumber.isSociable(12496, 5));
27+
}
28+
29+
@Test
30+
public void testAmicableNumbersAreSociableOfLengthTwo() {
31+
assertTrue(SociableNumber.isSociable(220, 2));
32+
assertTrue(SociableNumber.isSociable(284, 2));
33+
}
34+
35+
@Test
36+
public void testNonSociableNumbers() {
37+
assertFalse(SociableNumber.isSociable(12, 5));
38+
assertFalse(SociableNumber.isSociable(10, 3));
39+
}
40+
41+
@Test
42+
public void testEarlyCycleReturn() {
43+
// 220 has cycle length 2; requesting a different length should return
44+
// false because it returns to the start too early.
45+
assertFalse(SociableNumber.isSociable(220, 3));
46+
assertFalse(SociableNumber.isSociable(284, 4));
47+
assertFalse(SociableNumber.isSociable(12496, 3));
48+
}
49+
50+
@Test
51+
public void testInvalidInputs() {
52+
assertFalse(SociableNumber.isSociable(0, 5));
53+
assertFalse(SociableNumber.isSociable(-1, 5));
54+
assertFalse(SociableNumber.isSociable(220, 1));
55+
}
56+
}

0 commit comments

Comments
 (0)