Create new instant answer for combinations and permutations
parent
71dedeac11
commit
3798fb6dab
|
@ -0,0 +1,90 @@
|
||||||
|
package DDG::Goodie::Combination;
|
||||||
|
# ABSTRACT: Compute combinations and permutations
|
||||||
|
# Start at https://duck.co/duckduckhack/goodie_overview if you are new
|
||||||
|
# to instant answer development
|
||||||
|
|
||||||
|
use DDG::Goodie;
|
||||||
|
with 'DDG::GoodieRole::NumberStyler';
|
||||||
|
|
||||||
|
zci answer_type => "combination";
|
||||||
|
zci is_cached => 1;
|
||||||
|
|
||||||
|
name "Combination";
|
||||||
|
description "Computes combinations and permutations.";
|
||||||
|
primary_example_queries "10 choose 3", "25 permute 16";
|
||||||
|
secondary_example_queries "16 permutation 3";
|
||||||
|
category "calculations";
|
||||||
|
topics "math";
|
||||||
|
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Combination.pm";
|
||||||
|
attribution github => ["richardscollin", "Collin Richards"];
|
||||||
|
|
||||||
|
triggers any => "any", "choose", "permute", "permutation";
|
||||||
|
|
||||||
|
my $number_re = number_style_regex();
|
||||||
|
|
||||||
|
# Handle statement
|
||||||
|
handle query => sub {
|
||||||
|
my $query = $_;
|
||||||
|
return unless /^($number_re) (choose|permute|permutation) ($number_re)$/i;
|
||||||
|
|
||||||
|
my $style = number_style_for($1,$3);
|
||||||
|
return unless $style; #Cannot determine number format
|
||||||
|
my $operation = lc $2;
|
||||||
|
|
||||||
|
if ($operation eq 'permutation') {
|
||||||
|
$operation = 'permute';#standardizes output for tests
|
||||||
|
}
|
||||||
|
|
||||||
|
my $p1 = $style->for_computation($1);
|
||||||
|
my $p2 = $style->for_computation($3);
|
||||||
|
|
||||||
|
#Ensure both are non-negative integers
|
||||||
|
return unless $p1 =~ /^\d+$/ && $p2 =~ /^\d+$/;
|
||||||
|
|
||||||
|
#Do not try to calculate undefined combinations
|
||||||
|
return unless $p1 >= $p2;
|
||||||
|
|
||||||
|
my $result;
|
||||||
|
my %struct_ans;
|
||||||
|
|
||||||
|
if ('choose' eq $operation) {
|
||||||
|
$result = $style->for_display(choose($p1, $p2));
|
||||||
|
} else { #must be permute
|
||||||
|
$result = $style->for_display(permute($p1, $p2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result,
|
||||||
|
structured_answer => {
|
||||||
|
input => [$style->for_display($p1) . " $operation " . $style->for_display($p2)],
|
||||||
|
operation => $operation,
|
||||||
|
result => $style->with_html($result),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#Computes the cumulative product of the numbers from $_[0] to $_[1] inclusive
|
||||||
|
#Do not call when first parameter is greater then second.
|
||||||
|
sub cumprod {
|
||||||
|
my $acc = 1;
|
||||||
|
for (my $i = $_[0]; $i <= $_[1]; $i++) {
|
||||||
|
$acc *= $i;
|
||||||
|
}
|
||||||
|
return $acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub choose {
|
||||||
|
my ($n, $k) = @_;
|
||||||
|
|
||||||
|
#optimiztion combination is semetric
|
||||||
|
my $diff = $n - $k;
|
||||||
|
if ($k > $diff) {
|
||||||
|
$k = $diff;
|
||||||
|
}
|
||||||
|
return cumprod($n - $k + 1, $n) / cumprod(1, $k);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub permute {
|
||||||
|
my ($n, $k) = @_;
|
||||||
|
return cumprod($n - $k + 1, $n);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Test::More;
|
||||||
|
use DDG::Test::Goodie;
|
||||||
|
|
||||||
|
zci answer_type => "combination";
|
||||||
|
zci is_cached => 1;
|
||||||
|
|
||||||
|
ddg_goodie_test(
|
||||||
|
[qw( DDG::Goodie::Combination )],
|
||||||
|
# At a minimum, be sure to include tests for all:
|
||||||
|
# - primary_example_queries
|
||||||
|
# - secondary_example_queries
|
||||||
|
'10 choose 3' => test_zci('120',
|
||||||
|
structured_answer => {
|
||||||
|
input => ['10 choose 3'],
|
||||||
|
operation => 'choose',
|
||||||
|
result => '120',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'25 permute 16' => test_zci('4.27447366714368 * 10^19',
|
||||||
|
structured_answer => {
|
||||||
|
input => ["25 permute 16"],
|
||||||
|
operation => "permute",
|
||||||
|
result => "4.27447366714368 * 10<sup>19</sup>"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'16 permutation 3' => test_zci('3,360',
|
||||||
|
structured_answer => {
|
||||||
|
input => ["16 permute 3"],
|
||||||
|
operation => "permute",
|
||||||
|
result => "3,360"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'15 permutation 0' => test_zci('1',
|
||||||
|
structured_answer => {
|
||||||
|
input => ["15 permute 0"],
|
||||||
|
operation => "permute",
|
||||||
|
result => "1"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'1,000 choose 2' => test_zci('499,500',
|
||||||
|
structured_answer => {
|
||||||
|
input => ["1,000 choose 2"],
|
||||||
|
operation => "choose",
|
||||||
|
result => "499,500"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'0 choose 100' => undef,
|
||||||
|
'10 choose 100' => undef,
|
||||||
|
'10.5 choose 1' => undef,
|
||||||
|
'1.000,5 choose 2' => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
done_testing;
|
Loading…
Reference in New Issue