--- id: 5949b579404977fbaefcd737 title: 友愛数 challengeType: 5 forumTopicId: 302225 dashedName: amicable-pairs --- # --description-- 2つの整数、$N$と$M$は、 $N \\neq M$であり、[真の約数](https://rosettacode.org/wiki/Proper divisors "Proper divisors") の合計に関して、$N$ ($\\mathrm{sum}(\\mathrm{propDivs}(N))$) $= M$ かつ $\\mathrm{sum}(\\mathrm{propDivs}(M)) = N$である場合に、[友愛数](https://en.wikipedia.org/wiki/Amicable numbers "wp: Amicable numbers")です。 **例:** **1184** と **1210** は、真の約数の和から友愛数だと分かります。 # --instructions-- 計算して、20,000 以下の 友愛数を表示します (8 つあります)。 # --hints-- `amicablePairsUpTo` という関数です。 ```js assert(typeof amicablePairsUpTo === 'function'); ``` `amicablePairsUpTo(300)` は `[[220,284]]` を返します。 ```js assert.deepEqual(amicablePairsUpTo(300), answer300); ``` `amicablePairsUpTo(3000)` は `[[220,284],[1184,1210],[2620,2924]]` を返します。 ```js assert.deepEqual(amicablePairsUpTo(3000), answer3000); ``` `amicablePairsUpTo(20000)` は `[[220,284],[1184,1210],[2620,2924],[5020,5564],[6232,6368],[10744,10856],[12285,14595],[17296,18416]]` を返します。 ```js assert.deepEqual(amicablePairsUpTo(20000), answer20000); ``` # --seed-- ## --after-user-code-- ```js const answer300 = [[220, 284]]; const answer3000 = [ [220, 284], [1184, 1210], [2620, 2924] ]; const answer20000 = [ [220, 284], [1184, 1210], [2620, 2924], [5020, 5564], [6232, 6368], [10744, 10856], [12285, 14595], [17296, 18416] ]; ``` ## --seed-contents-- ```js function amicablePairsUpTo(maxNum) { return true; } ``` # --solutions-- ```js // amicablePairsUpTo :: Int -> [(Int, Int)] function amicablePairsUpTo(maxNum) { return range(1, maxNum) .map(x => properDivisors(x) .reduce((a, b) => a + b, 0)) .reduce((a, m, i, lst) => { const n = i + 1; return (m > n) && lst[m - 1] === n ? a.concat([ [n, m] ]) : a; }, []); } // properDivisors :: Int -> [Int] function properDivisors(n) { if (n < 2) return []; const rRoot = Math.sqrt(n); const intRoot = Math.floor(rRoot); const blnPerfectSquare = rRoot === intRoot; const lows = range(1, intRoot) .filter(x => (n % x) === 0); return lows.concat(lows.slice(1) .map(x => n / x) .reverse() .slice(blnPerfectSquare | 0)); } // Int -> Int -> Maybe Int -> [Int] function range(m, n, step) { const d = (step || 1) * (n >= m ? 1 : -1); return Array.from({ length: Math.floor((n - m) / d) + 1 }, (_, i) => m + (i * d)); } ```