Combining Combinations and Permutations
Combining Combinations and Permutations in PHP: From Mathematical Concept to a 90-Day Implementation Plan
Combinations and permutations are often introduced as mathematical concepts: choose a number of elements, arrange them in different orders, and calculate how many possible results exist. In software development, however, these concepts become practical tools for building search systems, product configurators, scheduling engines, testing utilities, recommendation logic, and structured data-generation workflows.
The important step is moving from theory to an implementation that can be tested, reused, and controlled.
This guide turns the concept into a practical 90-day development worksheet. The objective is not simply to understand what combinations and permutations mean. The objective is to build a reusable PHP process that can take a dynamic array, generate combinations of different sizes, generate permutations for each combination, and transform those results into useful application data.
1. The Core Concept: Selection First, Ordering Second
There are two separate operations.
Combination means selecting elements without considering their order.
Permutation means arranging selected elements where order matters.
Consider this array:
$items = [3, 10, 15, 20]; If we select two elements, the combinations are:
[3, 10] [3, 15] [3, 20] [10, 15] [10, 20] [15, 20] Notice that [3, 10] and [10, 3] are not both listed. They represent the same combination.
Once a combination is selected, permutation generation can produce different orders.
[3, 10] → [3, 10] → [10, 3] This gives us a two-stage processing model:
Source Array ↓ Generate Combinations ↓ Generate Permutations ↓ Transform Results ↓ Store / Display / Process Keeping these stages separate is one of the most important design decisions in the entire implementation.
2. Why Combine Both Techniques?
A system may need more than full permutations of the entire array.
For example, with four values, you may want:
- Every possible pair.
- Every possible ordering of every pair.
- Every possible group of three.
- Every possible ordering of every group of three.
- The full four-element ordering.
The system therefore needs to iterate through different combination lengths.
$items = [3, 10, 15, 20]; for ($length = 2; $length <= count($items); $length++) { $combinations = combine($items, $length); foreach ($combinations as $combination) { $permutations = permute($combination); foreach ($permutations as $permutation) { $value = implode('', $permutation); // Process $value } } } This small structure demonstrates a broader software principle: use one reusable algorithm for each responsibility, then compose the algorithms into a larger workflow.
3. Worksheet Step 1: Define the Input
Before implementing anything, write down the expected input.
Input Worksheet
- How many elements can the input contain?
- Are elements integers, strings, or both?
- Can duplicate values exist?
- Does the original order matter?
- What is the minimum combination size?
- What is the maximum combination size?
- What should happen with invalid input?
For example:
$items = [3, 10, 15, 20]; Then define:
Minimum length = 2 Maximum length = 4 Duplicates = not allowed Output = concatenated strings This specification prevents the implementation from becoming ambiguous.
4. Worksheet Step 2: Normalize the Data
Data should be normalized before expensive processing.
If duplicate values are not meaningful, remove them first:
$items = array_values(array_unique($items)); This matters because permutation generation can multiply duplicate input into duplicate results.
For example:
$items = [3, 10, 10, 15]; If the two 10 values represent the same logical item, the correct processing input is:
[3, 10, 15] The rule must be explicit, however. If repeated values carry meaning, they should not automatically be removed.
5. Worksheet Step 3: Build the Combination Function
The combination function should be reusable regardless of whether you need two, three, four, or more elements.
function combine(array $items, int $length): array { $result = []; if ($length === 0) { return [[]]; } if ($length > count($items)) { return []; } foreach ($items as $index => $item) { $remaining = array_slice($items, $index + 1); foreach (combine($remaining, $length - 1) as $combination) { array_unshift($combination, $item); $result[] = $combination; } } return $result; } The important design feature is the $length parameter.
Instead of creating separate functions such as:
combineTwo() combineThree() combineFour() the application has one generalized function:
combine($items, 2); combine($items, 3); combine($items, 4); 6. Worksheet Step 4: Build the Permutation Function
After selecting a combination, generate all possible orderings.
function permute(array $items): array { if (count($items) <= 1) { return [$items]; } $result = []; foreach ($items as $index => $item) { $remaining = $items; unset($remaining[$index]); $remaining = array_values($remaining); foreach (permute($remaining) as $permutation) { array_unshift($permutation, $item); $result[] = $permutation; } } return $result; } The function recursively chooses an element as the first item and then generates every possible ordering of the remaining elements.
This is a direct example of recursive problem solving: solve a smaller version of the same problem until the base case is reached.
7. Worksheet Step 5: Connect the Two Algorithms
Now the two independent components can be combined.
$items = [3, 10, 15, 20]; $items = array_values(array_unique($items)); $results = []; for ($length = 2; $length <= count($items); $length++) { $combinations = combine($items, $length); foreach ($combinations as $combination) { $permutations = permute($combination); foreach ($permutations as $permutation) { $results[] = implode('', $permutation); } } } The result is a collection of concatenated values representing every requested ordering.
This is the point where mathematical notation becomes an executable software workflow.
8. Worksheet Step 6: Separate Data From Presentation
Do not make the algorithm responsible for deciding how results should be displayed.
Instead of generating only strings, retain structured data internally:
[ [3, 10], [10, 3], [3, 15], [15, 3] ] Then transform the values later:
$value = implode('', $permutation); This gives the application flexibility.
The same generated data could later become:
310 or:
3-10 or JSON:
{ "items": [3, 10] } without changing the underlying combination or permutation logic.
9. A Practical Local-Business Example
Imagine a local food-delivery business wants to test combinations of promotional categories. Its internal system has four categories represented by identifiers:
$categories = [3, 10, 15, 20]; The marketing team may want to evaluate pairs, triples, and four-category sequences.
Instead of manually creating hundreds of possibilities, the application can generate them automatically.
The technical workflow is:
Category IDs ↓ Validate ↓ Remove invalid / duplicate values ↓ Select groups ↓ Generate orderings ↓ Create test combinations ↓ Send to internal campaign workflow A local retail brand could use the same architecture for product bundles, category experiments, or promotional rule testing. The specific business domain changes; the algorithm does not.
10. Another Example: Local E-Commerce Product Bundles
Consider a small online store with four product identifiers:
$products = [101, 205, 310, 420]; The application may need to evaluate two-product and three-product bundles.
Combinations answer:
Which products can be grouped together?
Permutations answer:
In which possible orders can those products be represented?
This distinction becomes useful when the order itself influences the application's logic, ranking, URL generation, recommendation rules, or test cases.
11. The 90-Day Implementation Plan
Days 1–30: Understand and Prototype
The first month should focus on correctness rather than infrastructure.
Week 1: Mathematical Model
- Define combinations.
- Define permutations.
- Calculate expected result counts.
- Document duplicate behavior.
Week 2: PHP Prototype
Implement:
combine() permute() normalize() transform() Keep everything local and simple.
Week 3: Test Cases
Test:
[] [10] [10, 15] [10, 15, 20] [10, 15, 20, 30] [10, 10, 15] Verify the number and content of generated results.
Week 4: Refactoring
Remove duplicated code. Separate responsibilities. Add clear function signatures and error handling.
12. Days 31–60: Integration
Week 5: Connect the Data Source
Retrieve data from the application's actual source.
$items = getItems(); $items = normalizeItems($items); $results = generateResults($items); Do not put database queries inside recursive functions.
Week 6: Add Business Rules
Define:
- Minimum combination size.
- Maximum combination size.
- Duplicate policy.
- Invalid-value policy.
- Maximum input size.
Week 7: API or Application Integration
If the feature is exposed through an API, define the request and response format.
{ "items": [3, 10, 15, 20], "min_length": 2, "max_length": 4 } The response should have a documented structure and predictable error behavior.
Week 8: Performance Testing
Measure execution time and memory usage using increasingly large inputs.
Do not assume the algorithm will remain practical as the input grows.
13. Days 61–90: Production Readiness
Week 9: Optimization
Optimize only after measuring the implementation.
Possible improvements include:
- Deduplicating input earlier.
- Reducing unnecessary array copies.
- Using generators for large result sets.
- Moving large workloads to background jobs.
Week 10: Monitoring
Track:
- Input size.
- Number of generated results.
- Execution time.
- Memory consumption.
- Failed requests.
Week 11: Security and Limits
Never allow unrestricted user input to trigger unlimited combinatorial processing.
For example:
if (count($items) > $maximumAllowed) { throw new InvalidArgumentException( 'Input exceeds the supported limit.' ); } Week 12: Documentation and Handover
Document:
- Function interfaces.
- Input rules.
- Output format.
- Complexity.
- Performance limits.
- Test cases.
- Deployment requirements.
14. Complexity Must Be Part of the Plan
The number of full permutations for n unique elements is:
n! Therefore:
4! = 24 5! = 120 6! = 720 7! = 5,040 8! = 40,320 9! = 362,880 10! = 3,628,800 When combinations of different sizes are also generated, the total workload increases further.
This is why the 90-day plan includes performance testing before production deployment.
The algorithm may be completely correct while the business requirement is still operationally unrealistic.
15. Free Tools to Use Before Spending Money
For the initial development phase, you can accomplish most of the work using standard development tools:
- PHP runtime.
- A local development environment.
- PHP unit testing tools.
- Version control.
- Command-line benchmarking.
- Basic logging.
You do not need to purchase specialized infrastructure simply to understand whether a recursive algorithm works.
Spend money when the problem becomes one of scale, reliability, integration, or operational support—not merely because the algorithm looks mathematical.
16. What Should Be Delegated?
For a small project, the core algorithm is an excellent candidate for self-development.
You can reasonably build and test:
combine() permute() normalize() basic tests basic output transformation Consider professional development support when the surrounding requirements involve:
- Production API architecture.
- Authentication.
- Large-scale background processing.
- Database optimization.
- Cloud deployment.
- Monitoring.
- Security requirements.
- High-volume workloads.
The principle is simple: do the understandable core yourself; delegate complexity that carries operational risk.
17. Senior Developer Insight
The most valuable lesson is not how to write two recursive functions. It is how to compose simple algorithms into a controlled processing pipeline.
A weak implementation starts with:
"Generate everything." A stronger implementation starts with:
What is the input? What is a valid combination? Does order matter? Are duplicates meaningful? How many results are expected? Can the results fit in memory? What happens when the input grows? Only after answering those questions should the implementation begin.
The architecture should remain modular:
┌──────────────┐ │ Raw Input │ └──────┬───────┘ ↓ ┌──────────────┐ │ Validation │ └──────┬───────┘ ↓ ┌──────────────┐ │ Normalization│ └──────┬───────┘ ↓ ┌──────────────┐ │ Combinations │ └──────┬───────┘ ↓ ┌──────────────┐ │ Permutations │ └──────┬───────┘ ↓ ┌──────────────┐ │ Transformation│ └──────┬───────┘ ↓ ┌──────────────┐ │ Application │ └──────────────┘ Each stage has one responsibility. That makes the system easier to test, replace, optimize, and reuse.
18. Final 90-Day Worksheet
| Period | Objective | Deliverable |
|---|---|---|
| Days 1–7 | Define the mathematical requirements | Technical specification |
| Days 8–14 | Build combination logic | Reusable PHP function |
| Days 15–21 | Build permutation logic | Reusable PHP function |
| Days 22–30 | Validate correctness | Automated test suite |
| Days 31–45 | Integrate application data | Working application integration |
| Days 46–60 | Define operational rules | Validation and limits |
| Days 61–75 | Benchmark and optimize | Performance report |
| Days 76–84 | Prepare production operation | Monitoring and error handling |
| Days 85–90 | Finalize delivery | Documentation and deployment checklist |
19. Final Checklist
Before considering the implementation complete, confirm:
- Combination and permutation logic are separate.
- Combination length is configurable.
- Duplicate handling is explicitly defined.
- Invalid input is rejected safely.
- The original input is not unexpectedly modified.
- Output formatting is separate from generation.
- Automated tests cover normal and edge cases.
- Expected result volume has been calculated.
- Large inputs have defined limits.
- Performance has been measured rather than guessed.
- Large workloads have an appropriate processing strategy.
- Documentation explains how the functions should be used.
Conclusion
Combining combinations and permutations is a useful example of how mathematical concepts become practical software architecture.
The process starts with selection: determine which groups of elements are valid. It then moves to ordering: generate the possible arrangements of each selected group. Finally, the results are transformed into whatever format the application requires.
The strongest implementation does not stop at producing correct output. It defines the input, handles duplicates, separates responsibilities, tests edge cases, measures complexity, and establishes limits before the feature reaches production.
A 90-day approach provides enough time to move through these stages deliberately: prototype first, integrate second, optimize third, and operationalize last.
The resulting PHP code may be relatively small. The engineering discipline surrounding that code is what makes it reusable.
