O'Halloran numbers
You are encouraged to solve this task according to the task description, using any language you may know.
For this task, for our purposes, a cuboid is a 3 dimensional object, with six rectangular faces, where all angles are right angles, opposite faces of the cuboid are equal, and where each dimension is a positive integer unit length. It will subsequently be referred to simply as a cuboid; but be aware that it references the above definition.
The surface area of a cuboid is two times the length times the width, plus two times the length times the height, plus two times the width times the height. A cuboid will always have an even integer surface area. The minimum surface area a cuboid may have is 6; one where the l, w, and h measurements are all 1.
2 × ( l × w + w × h + h × l ) 2 × ( 1 × 1 + 1 × 1 + 1 × 1 ) = 6
Different cuboid configurations (may) yield different surface areas, but the surface area is always an integer and is always even.
A cuboid with l = 2, w = 1 h = 1 has a surface area of 10
2 × ( 2 × 1 + 1 × 1 + 1 × 2 ) = 10
There is no configuration which will yield a surface area of 8.
There are 16 known even integer values below 1000 which can not be a surface area for any integer cuboid. It is conjectured, though not rigorously proved, that no others exist.
- Task
- Find and display the sixteen even integer values larger than 6 (the minimum cuboid area) and less than 1000
that can not be the surface area of a cuboid.
- See also
ALGOL 68
As in the Phix and other samples, we only need to consider one arrangement of each l, b and h values.
BEGIN # find O'Halloran numbers - numbers that cannot be the surface area of #
# a cuboid with integer dimensions #
INT count := 0;
INT max area = 1 000;
INT half max area = max area OVER 2;
FOR n FROM 8 BY 2 TO max area DO
BOOL ohalloran := TRUE;
FOR l TO half max area WHILE ohalloran DO
FOR b FROM l TO half max area WHILE INT lb = l * b;
lb < n AND ohalloran
DO
FOR h FROM b TO half max area WHILE INT bh = b * h, lh = l * h;
INT sum = 2 * ( lb + bh + lh );
sum <= n AND ( ohalloran := sum /= n )
DO SKIP OD
OD
OD;
IF ohalloran THEN
print( ( " ", whole( n, 0 ) ) );
count +:= 1
FI
OD
END
- Output:
8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
AppleScript
on OHalloranNumbers(max)
script o
property evens : {missing value, missing value}
end script
repeat with n from 6 to max by 2
set end of o's evens to n
end repeat
set halfMax to max div 2
set sixthMax to halfMax div 3
repeat with x from 1 to sixthMax
repeat with y from x to (sixthMax div x)
repeat with halfArea from ((x + x + y) * y) to halfMax by (x + y)
set o's evens's item halfArea to missing value
end repeat
end repeat
end repeat
return o's evens's integers
end OHalloranNumbers
OHalloranNumbers(1000 - 1)
(* Repeat logic condensed from:
repeat with x from 1 to sixthMax
repeat with y from x to sixthMax
set xy to x * y
if (xy > sixthMax) then exit repeat
repeat with z from y to sixthMax
set halfArea to xy + (x + y) * z
if (halfArea > halfMax) then exit repeat
set o's evens's item halfArea to missing value
end repeat
end repeat
end repeat
*)
- Output:
{8, 12, 20, 36, 44, 60, 84, 116, 140, 156, 204, 260, 380, 420, 660, 924}
Arturo
found: select 6..998 => even?
loop 1..497 'l ->
loop 1..l 'w [
lw: l * w
if lw >= 498 -> break
loop 1..w 'h [
sa: 2 * (lw + (w*h) + h*l)
(sa < 1000)? -> 'found -- sa
-> break
]
]
print found
- Output:
8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
BASIC
Rosetta Code problem: https://rosettacode.org/wiki/O%27Halloran_numbers
by Jjuanhdez, 02/2023
BASIC256
maxArea = 1000
halfMaxArea = maxArea/2
dim T[halfMaxArea] #table of half areas
for i = 0 to halfMaxArea-1
T[i] = True #assume all are O'kalloran numbers
next i
for l = 1 to maxArea
for w = 1 to halfMaxArea
for h = 1 to halfMaxArea
suma = l*w + l*h + w*h
if suma < halfMaxArea then T[suma] = False #not an O'kalloran number
next h
next w
next l
print "All known O'Halloran numbers:"
for l = 6/2 to halfMaxArea-1
if T[l] then print int(l*2); " ";
next l
- Output:
All known O'Halloran numbers: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Chipmunk Basic
100 cls
110 maxarea = 1000
120 halfmaxarea = maxarea/2
130 dim t(halfmaxarea)'table of half areas
140 for i = 0 to halfmaxarea-1
150 t(i) = 1 'assume all are O'kalloran numbers
160 next i
170 for l = 1 to maxarea
180 for w = 1 to halfmaxarea
190 for h = 1 to halfmaxarea
200 suma = l*w+l*h+w*h
210 if suma < halfmaxarea then t(suma) = 0 'not an O'kalloran number
220 next h
230 next w
240 next l
250 print "All known O'Halloran numbers:"
260 print "[ ";
270 for l = 6/2 to halfmaxarea-1
280 if t(l) then print int(l*2);" ";
290 next l
300 print chr$(8);"]"
310 end
FreeBASIC
Const maxArea = 1000, halfMaxArea = maxArea/2
Dim As Integer i, l, w, h, suma
Dim As Boolean T(halfMaxArea) 'table of half areas
For i = 0 To halfMaxArea-1
T(i) = True 'assume all are O'kalloran numbers
Next i
For l = 1 To maxArea
For w = 1 To halfMaxArea
For h = 1 To halfMaxArea
suma = l*w + l*h + w*h
If suma < halfMaxArea Then T(suma) = False 'not an O'kalloran number
Next h, w, l
Print "All known O'Halloran numbers:"
Print "[";
For l = 6/2 To halfMaxArea-1
If T(l) Then Print l*2; ",";
Next l
Print Chr(8); " ]"
Sleep
- Output:
All known O'Halloran numbers: [ 8, 12, 20, 36, 44, 60, 84, 116, 140, 156, 204, 260, 380, 420, 660, 924 ]
QBasic
The Chipmunk Basic solution works without any changes.
True BASIC
LET maxarea = 1000
LET halfmaxarea = maxarea/2
DIM t(1)
MAT REDIM t(0 TO halfmaxarea) !Table of half areas
FOR i = 0 TO halfmaxarea-1
LET t(i) = 1 !assume all are O'kalloran numbers
NEXT i
FOR l = 1 TO maxarea
FOR w = 1 TO halfmaxarea
FOR h = 1 TO halfmaxarea
LET suma = l*w+l*h+w*h
IF suma < halfmaxarea THEN LET t(suma) = 0 !not an O'kalloran number
NEXT h
NEXT w
NEXT l
PRINT "All known O'Halloran numbers:"
FOR l = 6/2 TO halfmaxarea-1
IF t(l) = 1 THEN PRINT INT(l*2);" ";
NEXT l
END
Yabasic
maxArea = 1000
halfMaxArea = maxArea/2
dim T(halfMaxArea) //table of half areas
for i = 0 to halfMaxArea-1
T(i) = True //assume all are O'kalloran numbers
next i
for l = 1 to maxArea
for w = 1 to halfMaxArea
for h = 1 to halfMaxArea
suma = l*w + l*h + w*h
if suma < halfMaxArea T(suma) = False //not an O'kalloran number
next h
next w
next l
print "All known O'Halloran numbers:"
print "[";
for l = 6/2 to halfMaxArea-1
if T(l) print l*2, ",";
next l
print chr$(8), "]"
- Output:
All known O'Halloran numbers: [8,12,20,36,44,60,84,116,140,156,204,260,380,420,660,924]
C
More or less.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int main() {
bool *found = calloc(1000, sizeof(bool)); // all false initially
int i, l, w, h, lw, sa;
for (l = 1; l < 498; ++l) {
for (w = 1; w <= l; ++w) {
lw = l * w;
if (lw >= 498) break;
for (h = 1; h <= w; ++h) {
sa = (lw + w*h + h*l) * 2;
if (sa < 1000) found[sa] = true;
}
}
}
printf("All known O'Halloran numbers:\n[");
for (i = 6; i < 1000; i += 2) {
if (!found[i]) printf("%d, ", i);
}
printf("\b\b]\n");
free(found);
return 0;
}
- Output:
All known O'Halloran numbers: [8, 12, 20, 36, 44, 60, 84, 116, 140, 156, 204, 260, 380, 420, 660, 924]
C++
#include <cstdint>
#include <iostream>
#include <vector>
int main() {
const uint32_t maximum_area = 1'000;
const uint32_t half_maximum_area = maximum_area / 2;
std::vector<uint32_t> ohalloran_numbers(half_maximum_area, true);
for ( uint32_t length = 1; length < maximum_area; ++length ) {
for ( uint32_t width = 1; width < half_maximum_area; ++width ) {
for ( uint32_t height = 1; height < half_maximum_area; ++height ) {
uint32_t half_area = length * width + length * height + width * height;
if ( half_area < half_maximum_area ) {
ohalloran_numbers[half_area] = false;
}
}
}
}
std::cout << "Values larger than 6 and less than 1_000 which cannot be the surface area of a cuboid:"
<< std::endl;
for ( uint32_t i = 3; i < half_maximum_area; ++i ) {
if ( ohalloran_numbers[i] ) {
std::cout << i * 2 << " ";
}
}
std::cout << std::endl;
}
- Output:
Values larger than 6 and less than 1_000 which cannot be the surface area of a cuboid: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Delphi
procedure ShowOHalloranNumbers(Memo: TMemo);
var L, W, H: integer;
var CubeArea, I, Cnt: integer;
const Limit = 1000;
const Limit2 = Limit div 2;
const Limit4 = Limit div 4;
var ValidOH: array [0..Limit2] of boolean;
var S: string;
begin
{Since we are using CuboidArea/2, use half the space }
for I:= 0 to Limit2-1 do ValidOH[I]:= true;
for L:= 1 to Limit4 do
for W:= 1 to L do
for H:= 1 to L do
begin
{Calculate 1/2 Cuboid value}
CubeArea:=L * W + L * H + W * H;
{Make sure number doesn't overrun array}
if CubeArea>=Length(ValidOH) then continue;
{Mark it as not an OHalloran number}
ValidOH[CubeArea]:= false;
end;
S:=''; Cnt:=0;
{Since we are using half the space}
{everything has to be doubled}
for I:=2 * 3 to Limit2-1 do
if ValidOH[I] then
begin
Inc(Cnt);
S:=S+Format('%8D',[I*2]);
If (Cnt mod 5)=0 then S:=S+CRLF;
end;
Memo.Lines.Add(S);
Memo.Lines.Add('Count='+IntToStr(Cnt));
end;
- Output:
12 20 36 44 60 84 116 140 156 204 260 380 420 660 924 Count=15 Elapsed Time: 6.353 ms.
EasyLang
len found[] 1000
for l = 1 to 497
for w = 1 to l
lw = l * w
if lw >= 498
break 1
.
for h = 1 to w
sa = (lw + w * h + h * l) * 2
if sa <= 1000
found[sa] = 1
.
.
.
.
for i = 6 step 2 to 998
if found[i] = 0
write i & " "
.
.
FutureBasic
_maxArea = 1000
_halfMaxArea = 500
NSUInteger i, l, w, h, sum
BOOL table( _halfMaxArea )
// Populate boolean table
for i = 0 to _halfMaxArea - 1
table( i ) = YES
next
for l = 1 to _maxArea
for w = 1 to _maxArea
for h = 1 to _halfMaxArea
sum = l * w + l * h + w * h
if sum < _halfMaxArea then table( sum ) = NO
next
next
next
printf @"All known O'Halloran numbers:"
for l = 6/2 to _halfMaxArea - 1
if table( l ) then print l * 2,
next
print chr$(8),
HandleEvents
- Output:
All known O'Halloran numbers: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Go
package main
import "fmt"
func main() {
found := make([]bool, 1000) // all false initially
for l := 1; l < 498; l++ {
for w := 1; w <= l; w++ {
lw := l * w
if lw >= 498 {
break
}
for h := 1; h <= w; h++ {
sa := (lw + w*h + h*l) * 2
if sa < 1000 {
found[sa] = true
}
}
}
}
fmt.Printf("All known O'Halloran numbers:\n[")
for i := 6; i < 1000; i += 2 {
if !found[i] {
fmt.Printf("%d, ", i)
}
}
fmt.Printf("\b\b]\n")
}
- Output:
All known O'Halloran numbers: [8, 12, 20, 36, 44, 60, 84, 116, 140, 156, 204, 260, 380, 420, 660, 924]
J
require'stats'
2*(3}.i.501)-.+/1 */\.(|:3 comb 42)-i:1
8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Here, we use combinations with repetitions to generate the various relevant cuboid side lengths. Then we multiply all three pairs of these side length combinations and sum the pairs. Then we remove these sums from the sequence 3..500, and finally we multiply the remaining 16 values by 2.
Java
import java.util.Arrays;
public final class OHalloranNumbers {
public static void main(String[] args) {
final int maximumArea = 1_000;
final int halfMaximumArea = maximumArea / 2;
boolean[] ohalloranNumbers = new boolean[halfMaximumArea];
Arrays.fill(ohalloranNumbers, true);
for ( int length = 1; length < maximumArea; length++ ) {
for ( int width = 1; width < halfMaximumArea; width++ ) {
for ( int height = 1; height < halfMaximumArea; height++ ) {
int halfArea = length * width + length * height + width * height;
if ( halfArea < halfMaximumArea ) {
ohalloranNumbers[halfArea] = false;
}
}
}
}
System.out.println("Values larger than 6 and less tha 1_000 which cannot be the surface area of a cuboid:");
for ( int i = 3; i < halfMaximumArea; i++ ) {
if ( ohalloranNumbers[i] ) {
System.out.print(i * 2 + " ");
}
}
System.out.println();
}
}
- Output:
Values larger than 6 and less than 1_000 which cannot be the surface area of a cuboid: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
JavaScript
// find O'Halloran numbers - numbers that cannot be the surface area of
// a cuboid with integer dimensions
'use strict'
const maxArea = 1000
const halfMaxArea = Math.trunc( maxArea / 2 )
let list = ""
for( let n = 8; n <= maxArea; n += 2 )
{
let oHalloran = true
for( let l = 1; l <= halfMaxArea && oHalloran; l ++ )
{
for( let b = l; b <= halfMaxArea; b ++ )
{
const lb = l * b
if( lb >= n || ! oHalloran )break
for( let h = b; h <= halfMaxArea; h ++ )
{
const bh = b * h, lh = l * h
const sum = 2 * ( lb + bh + lh )
oHalloran = sum != n
if( sum > n || ! oHalloran )break;
}
}
}
if( oHalloran )
{
list = list + " " + n
}
}
console.log( list )
- Output:
8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
jq
Also works with jaq and with gojq, the Go implementation of jq
# Emit a stream of possible cuboid areas less than or equal to the specified limit,
# $maxarea, which should be an even integer.
def cuboid_areas(maxarea):
maxarea as $maxarea
# min area per face is 1 so if total area is $maxarea, the max dimension is ($maxarea-4)/2
| ($maxarea/2) as $halfmax
| ($halfmax - 2) as $max
| foreach range(1; 1+$max) as $i (null;
label $loopj
# By symmetry, we can assume i <= j <= k
| foreach range($i; 1+$max) as $j (.;
($i*$j) as $ij
| if $ij + 2 > $halfmax then break $loopj else . end
| label $loopk
| foreach range($j; 1+$max) as $k (.;
($ij + $i*$k + $j*$k) as $total
| if $total > $halfmax then break $loopk
else 2 * $total
end) ) );
1000 as $n
| "Even integers greater than 6 but less than \($n) that cannot be cuboid surface areas:",
[range(6;$n;2)] - [cuboid_areas($n-2)]
Even integers greater than 6 but less than 1000 that cannot be cuboid surface areas: [8,12,20,36,44,60,84,116,140,156,204,260,380,420,660,924]
Julia
""" Rosetta code task: rosettacode.org/wiki/O%27Halloran_numbers """
const max_area, half_max = 1000, 500
const areas = trues(max_area)
areas[1:2:max_area] .= false
for i in 1:max_area
for j in 1:half_max
i * j > half_max && break
for k in 1:half_max
area = 2 * (i * j + i * k + j * k)
area > max_area && break
areas[area] = false
end
end
end
println("Even surface areas < $max_area NOT achievable by any regular integer-valued cuboid:\n",
[n for n in eachindex(areas) if areas[n]])
- Output:
Even surface areas < 1000 NOT achievable by any regular integer-valued cuboid: [2, 4, 8, 12, 20, 36, 44, 60, 84, 116, 140, 156, 204, 260, 380, 420, 660, 924]
Nim
import std/algorithm
const
M = 1000 # Maximum area.
N = M div 2 # Maximum half area.
var isOHalloran: array[3..N, bool]
isOHalloran.fill(true)
for length in 1..N:
for width in 1..length:
let plw = length * width
if plw > N: break
let slw = length + width
for height in 1..width:
let halfArea = plw + height * slw
if halfArea > N: break
isOHalloran[halfArea] = false
echo "All known O'Halloran numbers:"
for n in 3..N:
if isOHalloran[n]:
stdout.write 2 * n, ' '
echo()
- Output:
All known O'Halloran numbers: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Perl
use v5.36;
my @A;
my $threshold = 10_000; # redonkulous overkill
for my $x (1..$threshold) {
X: for my $y (1..$x) {
last X if $x*$y > $threshold;
Y: for my $z (1..$y) {
last Y if (my $area = 2 * ($x*$y + $y*$z + $z*$x)) > $threshold;
$A[$area] = "$x,$y,$z";
}
}
say 'Even integer surface areas NOT achievable by any regular, integer dimensioned cuboid';
for (0..$#A) {
print "$_ " if $_ < $threshold and $_ > 6 and 0 == $_ % 2 and not $A[$_];
}
- Output:
Even integer surface areas NOT achievable by any regular, integer dimensioned cuboid: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Phix
Since we are going to check {1,1,2}, there is no point checking {1,2,1} or {2,1,1} etc.
with javascript_semantics constant max_area = 1000, half_max = max_area/2 sequence areas = repeat(0,5)&tagset(max_area,6) for x=1 to max_area do if odd(x) then areas[x] = 0 end if for y=x to floor(half_max/x) do for z=y to half_max do atom area = 2 * (x * y + x * z + y * z) if area > max_area then exit end if areas[area] = 0 end for end for end for printf(1,"Even surface areas < %d NOT achievable by any regular integer-valued cuboid:\n%s\n", {max_area,join(filter(areas,"!=",0),fmt:="%d")})
- Output:
You can also set max_area to 1,000,000 and get no more results.
Even surface areas < 1000 NOT achievable by any regular integer-valued cuboid: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Python
#!/usr/bin/python
maxArea = 1000
halfMaxArea = 500
T = [] #table of half areas
for i in range(halfMaxArea):
T.append(True) #assume all are O'kalloran numbers
for l in range(1,maxArea):
for w in range(1, halfMaxArea):
for h in range(1, halfMaxArea):
suma = l*w + l*h + w*h
if suma < halfMaxArea:
T[suma] = False #not an O'kalloran number
print("All known O'Halloran numbers:")
for l in range(3,halfMaxArea):
if T[l]:
print(l*2, end=" ");
- Output:
All known O'Halloran numbers: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
PROMAL
;;; find O'Halloran numbers - numbers that cannot be the surface area of
;;; a cuboid with integer dimensions
PROGRAM OHalloran
INCLUDE LIBRARY
CON WORD maxArea = 1000
WORD count
WORD halfMaxArea
WORD n
WORD l
WORD b
WORD h
WORD lb
WORD lh
WORD bh
WORD sum
BYTE isOHalloran
BEGIN
count = 0
halfMaxArea = maxArea / 2
n = 8
WHILE n <= maxArea
isOHalloran = 1
l = 1
REPEAT
b = l
lb = l * b
REPEAT
h = b
REPEAT
bh = b * h
lh = l * h
sum = 2 * ( lb + ( h * ( b + l ) ) )
IF sum = n
isOHalloran = 0
h = h + 1
UNTIL h > halfMaxArea OR sum > n OR NOT isOHalloran
b = b + 1
lb = l * b
UNTIL b > halfMaxArea OR lb >= n OR NOT isOHalloran
l = l + 1
UNTIL l > halfMaxArea OR NOT isOHalloran
IF isOHalloran
OUTPUT " #W", n
count = count + 1
n = n + 2
END
- Output:
8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
Raku
my @Area;
my $threshold = 1000; # a little overboard to make sure we get them all
for 1..$threshold -> $x {
for 1..$x -> $y {
last if $x × $y > $threshold;
for 1..$y -> $z {
push @Area[my $area = ($x × $y + $y × $z + $z × $x) × 2], "$x,$y,$z";
last if $area > $threshold;
}
}
}
say "Even integer surface areas NOT achievable by any regular, integer dimensioned cuboid:\n" ~
@Area[^$threshold].kv.grep( { $^key > 6 and $key %% 2 and !$^value } )»[0];
- Output:
Even integer surface areas NOT achievable by any regular, integer dimensioned cuboid: 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924
RPL
« 1000 DUP 2 / FLOOR 1 → maxarea halfmax ohalloran
« { }
8 maxarea FOR n
1 ohalloran SF
WHILE DUP halfmax ≤ ohalloran FS? AND REPEAT
1 halfmax FOR b
DUP b *
IF DUP n ≥ ohalloran FC? OR THEN DROP halfmax 'b' STO
ELSE
b halfmax FOR h
OVER h * OVER + h b * + DUP +
IF DUP n == THEN ohalloran CF END
IF n > ohalloran FC? OR THEN halfmax 'h' STO END
NEXT DROP
END
NEXT
1 +
END DROP
IF ohalloran FS? THEN n + END
2 STEP
» » 'OHALL' STO
- Output:
1: { 8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924 }
Wren
import "./seq" for Lst
var found = []
for (l in 1..497) {
for (w in 1..l) {
var lw = l * w
if (lw >= 498) break
for (h in 1..w) {
var sa = (lw + w*h + h*l) * 2
if (sa < 1000) found.add(sa) else break
}
}
}
var allEven = (6..998).where { |i| i%2 == 0 }.toList
System.print("All known O'Halloran numbers:")
System.print(Lst.except(allEven, found))
- Output:
All known O'Halloran numbers: [8, 12, 20, 36, 44, 60, 84, 116, 140, 156, 204, 260, 380, 420, 660, 924]
XPL0
int L, W, H, HA, I;
char T(1000/2); \table of half areas
[for I:= 0 to 1000/2-1 do
T(I):= true; \assume all are O'Halloran numbers
for L:= 1 to 250 do
for W:= 1 to 250/L do
for H:= 1 to 250/L do
[HA:= L*W + L*H + W*H;
if HA < 500 then \not an O'Halloran number
T(HA):= false;
];
for I:= 6/2 to 1000/2-1 do
if T(I) then
[IntOut(0, I*2); ChOut(0, ^ )];
]
- Output:
8 12 20 36 44 60 84 116 140 156 204 260 380 420 660 924