[sf-perl] Farmer Puzzle
David Christensen
dpchrist at holgerdanske.com
Wed Aug 26 00:50:28 PDT 2026
On 8/25/26 22:41, Shlomi Fish wrote:
> Hi David,
Hi, Shlomi. Thank you for your insights.
> "my $a" and "my $b" are discouraged:
> https://perl-begin.org/tutorials/bad-elements/#vars-a-and-b
Thank you for the warning. It has been a while since I fell into that
trap. Avoiding lexical $a and $b sounds like a good habit.
Is there a way for an inner scope/block to undeclare/forget lexical
variables declared in an enclosing scope/block?
> On a different note: why aren't you working fully with integers? Products of
> cents or 50 cents.
The program uses floating point numbers because the problem statement
gave animal prices in dollars.
Adding "use integer" and doing the monetary math in cents both improves
performance and prevents floating point representation errors:
2026-08-26 00:36:26 dpchrist at laalaa ~/sandbox/perl/farmer-puzzle
$ expand dpchrist-integer-benchmark
#!/usr/bin/env perl
# $Id: dpchrist-integer-benchmark,v 1.2 2026/08/26 07:32:32 dpchrist Exp $
#
# A farmer goes to the fair. Cows cost $10.00 each, pigs cost $3.00
# each, and sheep cost $0.50 each. If the farmer wants to buy
# at least one cow, at least one pig, at least one sheep, and exactly
# 100 animals total, and wants to spend exactly $100.00, how many cows,
# pigs, and sheep should the farmer buy? Find all solutions.
#
# Perl program by David Paul Christensen <dpchrist at holgerdanske.com>
#
# Public Domain
use strict;
use warnings;
use integer;
use constant animals => 100;
use constant cow_cost => 1000;
use constant pig_cost => 300;
use constant sheep_cost => 50;
use constant budget => 10000;
sub buy_fair_animals
{
my @r;
my $max_cows = (budget - pig_cost - sheep_cost)/cow_cost;
foreach my $c (1 .. $max_cows) {
my $max_pigs = (budget - $c * cow_cost - sheep_cost)
/ pig_cost;
foreach my $p (1 .. $max_pigs) {
my $s = (budget - $c * cow_cost - $p * pig_cost)
/ sheep_cost;
my $total_animals = $c + $p + $s;
last if $total_animals > animals;
next if $total_animals != animals;
my $total_cost = $c * cow_cost
+ $p * pig_cost
+ $s * sheep_cost;
last if $total_cost > budget;
next if $total_cost != budget;
push @r, [$c, $p, $s];
}
}
return @r;
}
my $n = @ARGV ? shift : 100_000;
buy_fair_animals() for 1 .. $n - 1;
my @r = buy_fair_animals();
foreach (@r) {
my ($c, $p, $s) = @$_;
my $total_animals = $c + $p + $s;
my $total_cost = ($c * cow_cost + $p * pig_cost + $s * sheep_cost)
/ 100;
print "cows $c pigs $p sheep $s " .
"animals $total_animals cost $total_cost\n";
}
2026-08-26 00:36:34 dpchrist at laalaa ~/sandbox/perl/farmer-puzzle
$ time perl dpchrist-integer-benchmark
cows 5 pigs 1 sheep 94 animals 100 cost 100
real 0m0.957s
user 0m0.953s
sys 0m0.004s
2026-08-26 00:36:42 dpchrist at laalaa ~/sandbox/perl/farmer-puzzle
$ time perl dpchrist-integer-benchmark
cows 5 pigs 1 sheep 94 animals 100 cost 100
real 0m0.941s
user 0m0.937s
sys 0m0.004s
2026-08-26 00:36:43 dpchrist at laalaa ~/sandbox/perl/farmer-puzzle
$ time perl dpchrist-integer-benchmark
cows 5 pigs 1 sheep 94 animals 100 cost 100
real 0m0.978s
user 0m0.974s
sys 0m0.004s
But, integer variables impose a cost upon the programmer --I must
understand the minutia of every calculation to keep the term and
expression values within the range of integers.
Unfortunately, "use bigint" is not a good solution -- 100_000/200 = 500
times slower:
2026-08-26 00:42:58 dpchrist at laalaa ~/sandbox/perl/farmer-puzzle
$ time perl dpchrist-bigint-benchmark 200
cows 5 pigs 1 sheep 94 animals 100 cost 100
real 0m0.948s
user 0m0.947s
sys 0m0.001s
2026-08-26 00:43:02 dpchrist at laalaa ~/sandbox/perl/farmer-puzzle
$ time perl dpchrist-bigint-benchmark 200
cows 5 pigs 1 sheep 94 animals 100 cost 100
real 0m0.969s
user 0m0.960s
sys 0m0.009s
2026-08-26 00:43:06 dpchrist at laalaa ~/sandbox/perl/farmer-puzzle
$ time perl dpchrist-bigint-benchmark 200
cows 5 pigs 1 sheep 94 animals 100 cost 100
real 0m0.974s
user 0m0.970s
sys 0m0.005s
David
More information about the SanFrancisco-pm
mailing list