Skip to content

Commit d58f8a5

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

2 files changed

Lines changed: 116 additions & 0 deletions

File tree

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

0 commit comments

Comments
 (0)