Brilliant numbers

From Rosetta Code
Task
Brilliant numbers
You are encouraged to solve this task according to the task description, using any language you may know.

Brilliant numbers are a subset of semiprime numbers. Specifically, they are numbers that are the product of exactly two prime numbers that both have the same number of digits when expressed in base 10.

Brilliant numbers are useful in cryptography and when testing prime factoring algorithms.


E.G.
  • 3 × 3 (9) is a brilliant number.
  • 2 × 7 (14) is a brilliant number.
  • 113 × 691 (78083) is a brilliant number.
  • 2 × 31 (62) is semiprime, but is not a brilliant number (different number of digits in the two factors).


Task
  • Find and display the first 100 brilliant numbers.
  • For the orders of magnitude 1 through 6, find and show the first brilliant number greater than or equal to the order of magnitude, and, its position in the series (or the count of brilliant numbers up to that point).


Stretch
  • Continue for larger orders of magnitude.


See also

ALGOL 68

BEGIN # Find Brilliant numbers - semi-primes whose two prime factors have    #
      # the same number of digits                                            #
    PR read "primes.incl.a68" PR          # include prime utilities          #
    INT max prime = 1 010;                # maximum prime we will consider   #
                # should be enough to find the first brilliant number > 10^6 # 
    []BOOL prime = PRIMESIEVE max prime;  # sieve of primes to max prime     #
    # construct a table of brilliant numbers #
    [ 1 : max prime * max prime ]BOOL brilliant;
    FOR n FROM LWB brilliant TO UPB brilliant DO brilliant[ n ] := FALSE OD;
    # brilliant numbers where one of the fators is 2 #
    brilliant[ 4 ] := TRUE;
    FOR p FROM 3 BY 2 TO 7 DO brilliant[ 2 * p ] := TRUE OD;
    # brilliant numbers where both factors are odd #
    INT p start := 1, p end := 9;
    WHILE pstart < max prime DO
        FOR p FROM p start BY 2 TO p end DO
            IF prime[ p ] THEN
                brilliant[ p * p ] := TRUE;
                FOR q FROM p + 2 BY 2 TO p end DO
                    IF prime[ q ] THEN
                        brilliant[ p * q ] := TRUE
                    FI
                OD
            FI
        OD;
        p start := p end + 2;
        p end   := ( ( p start - 1 ) * 10 ) - 1;
        IF p end > max prime THEN p end := max prime FI
    OD;
    # show the first 100 brilliant numbers #
    INT b count := 0;
    FOR n TO UPB brilliant WHILE b count < 100 DO
        IF brilliant[ n ] THEN
            print( ( whole( n, -6 ) ) );
            IF ( b count +:= 1 ) MOD 10 = 0 THEN print( ( newline ) ) FI
        FI
    OD;
    # first brilliant number >= 10^n, n = 1, 2, ..., 6 #
    b count := 0;
    INT power of 10 := 10;
    FOR n TO UPB brilliant DO
        IF brilliant[ n ] THEN
            b count +:= 1;
            IF n >= power of 10 THEN
                print( ( "First brilliant number >= ", whole( power of 10, -8 )
                       , ": "                        , whole( n, -8 )
                       , " at position "             , whole( b count, -6 )
                       , newline
                       )
                     );
                power of 10 *:= 10
            FI
        FI
    OD
END
Output:
     4     6     9    10    14    15    21    25    35    49
   121   143   169   187   209   221   247   253   289   299
   319   323   341   361   377   391   403   407   437   451
   473   481   493   517   527   529   533   551   559   583
   589   611   629   649   667   671   689   697   703   713
   731   737   767   779   781   793   799   803   817   841
   851   869   871   893   899   901   913   923   943   949
   961   979   989  1003  1007  1027  1037  1067  1073  1079
  1081  1121  1139  1147  1157  1159  1189  1207  1219  1241
  1247  1261  1271  1273  1333  1343  1349  1357  1363  1369
First brilliant number >=       10:       10 at position      4
First brilliant number >=      100:      121 at position     11
First brilliant number >=     1000:     1003 at position     74
First brilliant number >=    10000:    10201 at position    242
First brilliant number >=   100000:   100013 at position   2505
First brilliant number >=  1000000:  1018081 at position  10538

AppleScript

use AppleScript version "2.3.1" -- Mac OS X 10.9 (Mavericks) or later.
use sorter : script "Insertion sort" -- <https://rosettacode.org/wiki/Sorting_algorithms/Insertion_sort#AppleScript>

on sieveOfEratosthenes(limit)
    set mv to missing value
    script o
        property numberList : makelist(limit, missing value)
    end script
    set o's numberList's item 2 to 2
    set o's numberList's item 3 to 3
    repeat with n from 5 to (limit - 2) by 6
        set o's numberList's item n to n
        tell (n + 2) to set o's numberList's item it to it
    end repeat
    if (limit - n > 5) then tell (n + 6) to set o's numberList's item it to it
    repeat with n from 5 to (limit ^ 0.5 div 1) by 6
        if (o's numberList's item n = n) then
            repeat with multiple from (n * n) to limit by n
                set o's numberList's item multiple to mv
            end repeat
        end if
        tell (n + 2)
            if (o's numberList's item it = it) then
                repeat with multiple from (it * it) to limit by it
                    set o's numberList's item multiple to mv
                end repeat
            end if
        end tell
    end repeat
    
    return o's numberList's numbers
end sieveOfEratosthenes

on makelist(limit, filler)
    if (limit < 1) then return {}
    script o
        property lst : {filler}
    end script
    
    set counter to 1
    repeat until (counter + counter > limit)
        set o's lst to o's lst & o's lst
        set counter to counter + counter
    end repeat
    if (counter < limit) then set o's lst to o's lst & o's lst's items 1 thru (limit - counter)
    return o's lst
end makelist

on join(lst, delim)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to delim
    set txt to lst as text
    set AppleScript's text item delimiters to astid
    return txt
end join

on ordinalise(n)
    set units to n mod 10
    if ((units > 3) or (n div 10 mod 10 is 1) or (units < 1) or (units mod 1 > 0)) then ¬
        return (n as text) & "th"
    return (n as text) & item units of {"st", "nd", "rd"}
end ordinalise

on task()
    script o
        -- Enough primes to include the first > the square root of 100,000,000.
        property primes : sieveOfEratosthenes(10020)
        -- Collector for enough not-quite-ordered brilliants to include the first 100.
        property first250 : {}
    end script
    -- List of data collectors for nine magnitudes.
    set {magData, mag, mv} to {{}, 1, missing value}
    repeat 9 times
        set magData's end to {magnitude:mag, lowest:mv, |count|:0}
        set mag to mag * 10
    end repeat
    
    -- Calculate the brilliant numbers and store the relevant info.
    set {primeMag, counter} to {1, 0}
    repeat with k from 1 to (count o's primes)
        set thisPrime to o's primes's item k
        if (thisPrime  primeMag) then
            set primeMag to primeMag * 10
            set i to k
        end if
        repeat with j from i to k
            set thisBrill to thisPrime * (o's primes's item j)
            if (counter < 250) then
                set counter to counter + 1
                set o's first250's end to thisBrill
            end if
            repeat with m from 9 to 1 by -1
                set theseData to magData's item m
                if (thisBrill  theseData's magnitude) then
                    if ((theseData's lowest is mv) or (theseData's lowest > thisBrill)) then ¬
                        set theseData's lowest to thisBrill
                    set theseData's |count| to (theseData's |count|) + 1
                    exit repeat
                end if
            end repeat
        end repeat
    end repeat
    
    -- Get the first 100 brilliants from the first 250 collected.
    tell sorter to sort(o's first250, 1, counter)
    set output to {"The first 100 brilliant numbers are:"}
    set theseTen to {}
    repeat with i from 1 to 100 by 10
        repeat with j from i to i + 9
            set theseTen's end to ("     " & o's first250's item j)'s text -6 thru end
        end repeat
        set output's end to join(theseTen, "")
        set theseTen to {}
    end repeat
    
    -- Get the data from the magnitude records.
    set counter to 1
    repeat with theseData in magData
        set output's end to "The first ≥ " & (theseData's magnitude) & ¬
            " is the " & ordinalise(counter) & ": " & (theseData's lowest)
        set counter to counter + (theseData's |count|)
    end repeat
    
    return join(output, linefeed)
end task

task()
Output:
"The first 100 brilliant numbers are:
     4     6     9    10    14    15    21    25    35    49
   121   143   169   187   209   221   247   253   289   299
   319   323   341   361   377   391   403   407   437   451
   473   481   493   517   527   529   533   551   559   583
   589   611   629   649   667   671   689   697   703   713
   731   737   767   779   781   793   799   803   817   841
   851   869   871   893   899   901   913   923   943   949
   961   979   989  1003  1007  1027  1037  1067  1073  1079
  1081  1121  1139  1147  1157  1159  1189  1207  1219  1241
  1247  1261  1271  1273  1333  1343  1349  1357  1363  1369
The first ≥ 1 is the 1st: 4
The first ≥ 10 is the 4th: 10
The first ≥ 100 is the 11th: 121
The first ≥ 1000 is the 74th: 1003
The first ≥ 10000 is the 242nd: 10201
The first ≥ 100000 is the 2505th: 100013
The first ≥ 1000000 is the 10538th: 1018081
The first ≥ 10000000 is the 124364th: 10000043
The first ≥ 100000000 is the 573929th: 100140049"

Arturo

brilliant?: function [x][
    pf: factors.prime x
    and? -> 2 = size pf
         -> equal? size digits first pf
                   size digits last pf
]

brilliants: new []
i: 2
while [100 > size brilliants][
    if brilliant? i -> 'brilliants ++ i
    i: i + 1
]

print "First 100 brilliant numbers:"
loop split.every: 10 brilliants 'row [
    print map to [:string] row 'item -> pad item 4
]
print ""

i: 4
nth: 0
order: 1
while [order =< 6] [
    if brilliant? i [
        nth: nth + 1
        if i >= 10^order [
            print ["First brilliant number >= 10 ^" order "is" i "at position" nth]
            order: order + 1
        ]
    ]

    i: i + 1
]
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49 
 121  143  169  187  209  221  247  253  289  299 
 319  323  341  361  377  391  403  407  437  451 
 473  481  493  517  527  529  533  551  559  583 
 589  611  629  649  667  671  689  697  703  713 
 731  737  767  779  781  793  799  803  817  841 
 851  869  871  893  899  901  913  923  943  949 
 961  979  989 1003 1007 1027 1037 1067 1073 1079 
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241 
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369 

First brilliant number >= 10 ^ 1 is 10 at position 4 
First brilliant number >= 10 ^ 2 is 121 at position 11 
First brilliant number >= 10 ^ 3 is 1003 at position 74 
First brilliant number >= 10 ^ 4 is 10201 at position 242 
First brilliant number >= 10 ^ 5 is 100013 at position 2505 
First brilliant number >= 10 ^ 6 is 1018081 at position 10538

C++

Library: Primesieve
#include <algorithm>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <locale>
#include <vector>

#include <primesieve.hpp>

auto get_primes_by_digits(uint64_t limit) {
    primesieve::iterator pi;
    std::vector<std::vector<uint64_t>> primes_by_digits;
    std::vector<uint64_t> primes;
    for (uint64_t p = 10; p <= limit;) {
        uint64_t prime = pi.next_prime();
        if (prime > p) {
            primes_by_digits.push_back(std::move(primes));
            p *= 10;
        }
        primes.push_back(prime);
    }
    return primes_by_digits;
}

int main() {
    std::cout.imbue(std::locale(""));

    auto start = std::chrono::high_resolution_clock::now();

    auto primes_by_digits = get_primes_by_digits(1000000000);

    std::cout << "First 100 brilliant numbers:\n";
    std::vector<uint64_t> brilliant_numbers;
    for (const auto& primes : primes_by_digits) {
        for (auto i = primes.begin(); i != primes.end(); ++i)
            for (auto j = i; j != primes.end(); ++j)
                brilliant_numbers.push_back(*i * *j);
        if (brilliant_numbers.size() >= 100)
            break;
    }
    std::sort(brilliant_numbers.begin(), brilliant_numbers.end());
    for (size_t i = 0; i < 100; ++i) {
        std::cout << std::setw(5) << brilliant_numbers[i]
                  << ((i + 1) % 10 == 0 ? '\n' : ' ');
    }

    std::cout << '\n';
    uint64_t power = 10;
    size_t count = 0;
    for (size_t p = 1; p < 2 * primes_by_digits.size(); ++p) {
        const auto& primes = primes_by_digits[p / 2];
        size_t position = count + 1;
        uint64_t min_product = 0;
        for (auto i = primes.begin(); i != primes.end(); ++i) {
            uint64_t p1 = *i;
            auto j = std::lower_bound(i, primes.end(), (power + p1 - 1) / p1);
            if (j != primes.end()) {
                uint64_t p2 = *j;
                uint64_t product = p1 * p2;
                if (min_product == 0 || product < min_product)
                    min_product = product;
                position += std::distance(i, j);
                if (p1 >= p2)
                    break;
            }
        }
        std::cout << "First brilliant number >= 10^" << p << " is "
                  << min_product << " at position " << position << '\n';
        power *= 10;
        if (p % 2 == 1) {
            size_t size = primes.size();
            count += size * (size + 1) / 2;
        }
    }

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration(end - start);
    std::cout << "\nElapsed time: " << duration.count() << " seconds\n";
}
Output:
First 100 brilliant numbers:
    4     6     9    10    14    15    21    25    35    49
  121   143   169   187   209   221   247   253   289   299
  319   323   341   361   377   391   403   407   437   451
  473   481   493   517   527   529   533   551   559   583
  589   611   629   649   667   671   689   697   703   713
  731   737   767   779   781   793   799   803   817   841
  851   869   871   893   899   901   913   923   943   949
  961   979   989 1,003 1,007 1,027 1,037 1,067 1,073 1,079
1,081 1,121 1,139 1,147 1,157 1,159 1,189 1,207 1,219 1,241
1,247 1,261 1,271 1,273 1,333 1,343 1,349 1,357 1,363 1,369

First brilliant number >= 10^1 is 10 at position 4
First brilliant number >= 10^2 is 121 at position 11
First brilliant number >= 10^3 is 1,003 at position 74
First brilliant number >= 10^4 is 10,201 at position 242
First brilliant number >= 10^5 is 100,013 at position 2,505
First brilliant number >= 10^6 is 1,018,081 at position 10,538
First brilliant number >= 10^7 is 10,000,043 at position 124,364
First brilliant number >= 10^8 is 100,140,049 at position 573,929
First brilliant number >= 10^9 is 1,000,000,081 at position 7,407,841
First brilliant number >= 10^10 is 10,000,600,009 at position 35,547,995
First brilliant number >= 10^11 is 100,000,000,147 at position 491,316,167
First brilliant number >= 10^12 is 1,000,006,000,009 at position 2,409,600,866
First brilliant number >= 10^13 is 10,000,000,000,073 at position 34,896,253,010
First brilliant number >= 10^14 is 100,000,380,000,361 at position 174,155,363,187
First brilliant number >= 10^15 is 1,000,000,000,000,003 at position 2,601,913,448,897
First brilliant number >= 10^16 is 10,000,001,400,000,049 at position 13,163,230,391,313
First brilliant number >= 10^17 is 100,000,000,000,000,831 at position 201,431,415,980,419

Elapsed time: 1.50048 seconds

Delphi

Works with: Delphi version 6.0


function Compare(P1,P2: pointer): integer;
{Compare for quick sort}
begin
Result:=Integer(P1)-Integer(P2);
end;

procedure GetBrilliantNumbers(List: TList; Limit: integer);
{Return specified number of Brilliant Numbers in list}
var I,J,P,Stop: integer;
var Sieve: TPrimeSieve;
begin
Sieve:=TPrimeSieve.Create;
try
{build twices as many primes}
Sieve.Intialize(Limit*2);
{Pair every n-digt prime with every n-digit prime}
I:=2;
while true do
	begin
	J:=I;
	{Put primes in J up to next power of 10 - 1}
	Stop:=Trunc(Power(10,Trunc(Log10(I))+1));
	while J<Stop do
		begin
		{Get the product}
		P:=I * J;
		{and store in the list}
		List.Add(Pointer(P));
		{Exit if we have all the numbers}
		if List.Count>=Limit then break;
		{Get next prime}
		J:=Sieve.NextPrime(J);
		end;
	{break out of outer loop if done}
	if List.Count>=Limit then break;
	{Get next prime}
	I:=Sieve.NextPrime(I);
	end;
{The list won't be in order, so sort them}
List.Sort(Compare);
finally Sieve.Free; end;
end;


procedure ShowBrilliantNumbers(Memo: TMemo);
var List: TList;
var S: string;
var I,D,P: integer;
begin
List:=TList.Create;
try
{Get 10 million brilliant numbers}
GetBrilliantNumbers(List,1000000);
{Show the first 100}
S:='';
for I:=0 to 100-1 do
	begin
	S:=S+Format('%7d',[Integer(List[I])]);
	if (I mod 10)=9 then S:=S+CRLF;
	end;
Memo.Lines.Add(S);
{Show additional information}
for D:=1 to 8 do
	begin
	P:=Trunc(Power(10,D));
	{Scan to find for 1st brilliant number >= 10^D }
	for I:=0 to List.Count-1 do
	 if Integer(List[I])>=P then break;
	{Display the info}
	S:=Format('First brilliant number >= 10^%d is %10d',[D,Integer(List[I])]);
	S:=S+Format(' at position %10D', [I]);
	Memo.Lines.Add(S);
	end;
finally List.Free; end;
end;
Output:
      4      6      9     10     14     15     21     25     35     49
    121    143    169    187    209    221    247    253    289    299
    319    323    341    361    377    391    403    407    437    451
    473    481    493    517    527    529    533    551    559    583
    589    611    629    649    667    671    689    697    703    713
    731    737    767    779    781    793    799    803    817    841
    851    869    871    893    899    901    913    923    943    949
    961    979    989   1003   1007   1027   1037   1067   1073   1079
   1081   1121   1139   1147   1157   1159   1189   1207   1219   1241
   1247   1261   1271   1273   1333   1343   1349   1357   1363   1369

First brilliant number >= 10^1 is         10 at position          3
First brilliant number >= 10^2 is        121 at position         10
First brilliant number >= 10^3 is       1003 at position         73
First brilliant number >= 10^4 is      10201 at position        241
First brilliant number >= 10^5 is     100013 at position       2504
First brilliant number >= 10^6 is    1018081 at position      10537
First brilliant number >= 10^7 is   10000043 at position     124363
First brilliant number >= 10^8 is  100140049 at position     573928
Elapsed Time: 185.451 ms.


Factor

Works with: Factor version 0.99 2022-04-03
USING: assocs formatting grouping io kernel lists lists.lazy
math math.functions math.primes.factors prettyprint
project-euler.common sequences ;

MEMO: brilliant? ( n -- ? )
    factors [ length 2 = ] keep
    [ number-length ] map all-eq? and ;

: lbrilliant ( -- list )
    2 lfrom [ brilliant? ] lfilter 1 lfrom lzip ;

: first> ( m -- n )
    lbrilliant swap '[ first _ >= ] lfilter car ;

: .first> ( n -- )
    dup first> first2
    "First brilliant number >= %7d: %7d at position %5d\n"
    printf ;

100 lbrilliant ltake list>array keys 10 group simple-table. nl
{ 1 2 3 4 5 6 } [ 10^ .first> ] each
Output:
4    6    9    10   14   15   21   25   35   49
121  143  169  187  209  221  247  253  289  299
319  323  341  361  377  391  403  407  437  451
473  481  493  517  527  529  533  551  559  583
589  611  629  649  667  671  689  697  703  713
731  737  767  779  781  793  799  803  817  841
851  869  871  893  899  901  913  923  943  949
961  979  989  1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First brilliant number >=      10:      10 at position     4
First brilliant number >=     100:     121 at position    11
First brilliant number >=    1000:    1003 at position    74
First brilliant number >=   10000:   10201 at position   242
First brilliant number >=  100000:  100013 at position  2505
First brilliant number >= 1000000: 1018081 at position 10538

FreeBASIC

function is_prime( n as uinteger ) as boolean
    if n = 2 then return true
    if n<2 or n mod 2 = 0 then return false
    for i as uinteger = 3 to sqr(n) step 2
        if (n mod i) = 0 then return false
    next i
    return true
end function

function first_prime_factor( n as uinteger ) as uinteger
    if n mod 2 = 0 then return 2
    for i as uinteger = 3 to sqr(n) step 2
        if (n mod i) = 0 then return i
    next i
    return n
end function

dim as uinteger count = 0, n = 0, ff, sf, expo = 0

while count<100
    ff = first_prime_factor(n)
    sf = n/ff
    if is_prime(sf) and len(str(ff)) = len(str(sf)) then
        print n,
        count = count + 1
        if count mod 6 = 0 then print
    end if
    n = n + 1
wend
print
count = 0

n = 0
do
    ff = first_prime_factor(n)
    sf = n/ff
    if is_prime(sf) and len(str(ff)) = len(str(sf)) then
        count = count + 1
        if n > 10^expo then
            print n;" is brilliant #"; count
            expo = expo + 1
            if expo = 9 then end
        end if
    end if
    n = n + 1
loop
4             6             9             10            14            15            
21            25            35            49            121           143           
169           187           209           221           247           253           
289           299           319           323           341           361           
377           391           403           407           437           451           
473           481           493           517           527           529           
533           551           559           583           589           611           
629           649           667           671           689           697           
703           713           731           737           767           779           
781           793           799           803           817           841           
851           869           871           893           899           901           
913           923           943           949           961           979           
989           1003          1007          1027          1037          1067          
1073          1079          1081          1121          1139          1147          
1157          1159          1189          1207          1219          1241          
1247          1261          1271          1273          1333          1343          
1349          1357          1363          1369          
4 is brilliant #1
14 is brilliant #5
121 is brilliant #11
1003 is brilliant #74
10201 is brilliant #242
100013 is brilliant #2505
1018081 is brilliant #10538
10000043 is brilliant #124364


Go

Translation of: Wren
Library: Go-rcu
package main

import (
    "fmt"
    "math"
    "rcu"
    "sort"
)

var primes = rcu.Primes(1e8 - 1)

type res struct {
    bc   interface{}
    next int
}

func getBrilliant(digits, limit int, countOnly bool) res {
    var brilliant []int
    count := 0
    pow := 1
    next := math.MaxInt
    for k := 1; k <= digits; k++ {
        var s []int
        for _, p := range primes {
            if p >= pow*10 {
                break
            }
            if p > pow {
                s = append(s, p)
            }
        }
        for i := 0; i < len(s); i++ {
            for j := i; j < len(s); j++ {
                prod := s[i] * s[j]
                if prod < limit {
                    if countOnly {
                        count++
                    } else {
                        brilliant = append(brilliant, prod)
                    }
                } else {
                    if next > prod {
                        next = prod
                    }
                    break
                }
            }
        }
        pow *= 10
    }
    if countOnly {
        return res{count, next}
    }
    return res{brilliant, next}
}

func main() {
    fmt.Println("First 100 brilliant numbers:")
    brilliant := getBrilliant(2, 10000, false).bc.([]int)
    sort.Ints(brilliant)
    brilliant = brilliant[0:100]
    for i := 0; i < len(brilliant); i++ {
        fmt.Printf("%4d ", brilliant[i])
        if (i+1)%10 == 0 {
            fmt.Println()
        }
    }
    fmt.Println()
    for k := 1; k <= 13; k++ {
        limit := int(math.Pow(10, float64(k)))
        r := getBrilliant(k, limit, true)
        total := r.bc.(int)
        next := r.next
        climit := rcu.Commatize(limit)
        ctotal := rcu.Commatize(total + 1)
        cnext := rcu.Commatize(next)
        fmt.Printf("First >= %18s is %14s in the series: %18s\n", climit, ctotal, cnext)
    }
}
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49 
 121  143  169  187  209  221  247  253  289  299 
 319  323  341  361  377  391  403  407  437  451 
 473  481  493  517  527  529  533  551  559  583 
 589  611  629  649  667  671  689  697  703  713 
 731  737  767  779  781  793  799  803  817  841 
 851  869  871  893  899  901  913  923  943  949 
 961  979  989 1003 1007 1027 1037 1067 1073 1079 
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241 
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369 

First >=                 10 is              4 in the series:                 10
First >=                100 is             11 in the series:                121
First >=              1,000 is             74 in the series:              1,003
First >=             10,000 is            242 in the series:             10,201
First >=            100,000 is          2,505 in the series:            100,013
First >=          1,000,000 is         10,538 in the series:          1,018,081
First >=         10,000,000 is        124,364 in the series:         10,000,043
First >=        100,000,000 is        573,929 in the series:        100,140,049
First >=      1,000,000,000 is      7,407,841 in the series:      1,000,000,081
First >=     10,000,000,000 is     35,547,995 in the series:     10,000,600,009
First >=    100,000,000,000 is    491,316,167 in the series:    100,000,000,147
First >=  1,000,000,000,000 is  2,409,600,866 in the series:  1,000,006,000,009
First >= 10,000,000,000,000 is 34,896,253,010 in the series: 10,000,000,000,073

Haskell

import Control.Monad (join)
import Data.Bifunctor (bimap)
import Data.List (intercalate, transpose)
import Data.List.Split (chunksOf, splitWhen)
import Data.Numbers.Primes (primeFactors)
import Text.Printf (printf)

-------------------- BRILLIANT NUMBERS -------------------

isBrilliant :: (Integral a, Show a) => a -> Bool
isBrilliant n = case primeFactors n of
  [a, b] -> length (show a) == length (show b)
  _ -> False

--------------------------- TEST -------------------------
main :: IO ()
main = do
  let indexedBrilliants =
        zip
          [1 ..]
          (filter isBrilliant [1 ..])

  putStrLn $
    table "  " $
      chunksOf 10 $
        show . snd
          <$> take 100 indexedBrilliants

  putStrLn "(index, brilliant)"
  mapM_ print $
    take 6 $
      fmap (fst . head) $
        splitWhen
          (uncurry (<) . join bimap (length . show . snd))
          $ zip indexedBrilliants (tail indexedBrilliants)

------------------------- DISPLAY ------------------------

table :: String -> [[String]] -> String
table gap rows =
  let ws = maximum . fmap length <$> transpose rows
      pw = printf . flip intercalate ["%", "s"] . show
   in unlines $ intercalate gap . zipWith pw ws <$> rows
Output:
   4     6     9    10    14    15    21    25    35    49
 121   143   169   187   209   221   247   253   289   299
 319   323   341   361   377   391   403   407   437   451
 473   481   493   517   527   529   533   551   559   583
 589   611   629   649   667   671   689   697   703   713
 731   737   767   779   781   793   799   803   817   841
 851   869   871   893   899   901   913   923   943   949
 961   979   989  1003  1007  1027  1037  1067  1073  1079
1081  1121  1139  1147  1157  1159  1189  1207  1219  1241
1247  1261  1271  1273  1333  1343  1349  1357  1363  1369

(index, brilliant)
(1,4)
(4,10)
(11,121)
(74,1003)
(242,10201)
(2505,100013)

J

oprimes=: {{ NB. all primes of order y
  p:(+i.)/-/\ p:inv +/\1 9*10^y
}}

obrill=: {{ NB. all brilliant numbers of order y primes
  ~.,*/~oprimes y
}}

brillseq=: {{ NB. sequences of brilliant numbers up through order y-1 primes
  /:~;obrill each i.y
}}

Task examples:

   10 10 $brillseq 2
   4    6    9   10   14   15   21   25   35   49
 121  143  169  187  209  221  247  253  289  299
 319  323  341  361  377  391  403  407  437  451
 473  481  493  517  527  529  533  551  559  583
 589  611  629  649  667  671  689  697  703  713
 731  737  767  779  781  793  799  803  817  841
 851  869  871  893  899  901  913  923  943  949
 961  979  989 1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369
NB. order, index, value
   (brillseq 4) (],.(I. 10^]) ([,.{) [) 1 2 3 4 5 6
1     3      10
2    10     121
3    73    1003
4   241   10201
5  2504  100013
6 10537 1018081

Stretch goal (results are order, index, value):

   (brillseq 4) (],.(I. 10^]) ([,.{) [) ,7
7 124363 10000043
   (brillseq 5) (],.(I. 10^]) ([,.{) [) 8 9
8  573928  100140049
9 7407840 1000000081

Java

Translation of: C++

Uses the PrimeGenerator class from Extensible prime generator#Java.

import java.util.*;

public class BrilliantNumbers {
    public static void main(String[] args) {
        var primesByDigits = getPrimesByDigits(100000000);
        System.out.println("First 100 brilliant numbers:");
        List<Integer> brilliantNumbers = new ArrayList<>();
        for (var primes : primesByDigits) {
            int n = primes.size();
            for (int i = 0; i < n; ++i) {
                int prime1 = primes.get(i);
                for (int j = i; j < n; ++j) {
                    int prime2 = primes.get(j);
                    brilliantNumbers.add(prime1 * prime2);
                }
            }
            if (brilliantNumbers.size() >= 100)
                break;
        }
        Collections.sort(brilliantNumbers);
        for (int i = 0; i < 100; ++i) {
            char c = (i + 1) % 10 == 0 ? '\n' : ' ';
            System.out.printf("%,5d%c", brilliantNumbers.get(i), c);
        }
        System.out.println();
        long power = 10;
        long count = 0;
        for (int p = 1; p < 2 * primesByDigits.size(); ++p) {
            var primes = primesByDigits.get(p / 2);
            long position = count + 1;
            long minProduct = 0;
            int n = primes.size();
            for (int i = 0; i < n; ++i) {
                long prime1 = primes.get(i);
                var primes2 = primes.subList(i, n);
                int q = (int)((power + prime1 - 1) / prime1);
                int j = Collections.binarySearch(primes2, q);
                if (j == n)
                    continue;
                if (j < 0)
                    j = -(j + 1);
                long prime2 = primes2.get(j);
                long product = prime1 * prime2;
                if (minProduct == 0 || product < minProduct)
                    minProduct = product;
                position += j;
                if (prime1 >= prime2)
                    break;
            }
            System.out.printf("First brilliant number >= 10^%d is %,d at position %,d\n",
                                p, minProduct, position);
            power *= 10;
            if (p % 2 == 1) {
                long size = primes.size();
                count += size * (size + 1) / 2;
            }
        }
    }

    private static List<List<Integer>> getPrimesByDigits(int limit) {
        PrimeGenerator primeGen = new PrimeGenerator(100000, 100000);
        List<List<Integer>> primesByDigits = new ArrayList<>();
        List<Integer> primes = new ArrayList<>();
        for (int p = 10; p <= limit; ) {
            int prime = primeGen.nextPrime();
            if (prime > p) {
                primesByDigits.add(primes);
                primes = new ArrayList<>();
                p *= 10;
            }
            primes.add(prime);
        }
        return primesByDigits;
    }
}
Output:
First 100 brilliant numbers:
    4     6     9    10    14    15    21    25    35    49
  121   143   169   187   209   221   247   253   289   299
  319   323   341   361   377   391   403   407   437   451
  473   481   493   517   527   529   533   551   559   583
  589   611   629   649   667   671   689   697   703   713
  731   737   767   779   781   793   799   803   817   841
  851   869   871   893   899   901   913   923   943   949
  961   979   989 1,003 1,007 1,027 1,037 1,067 1,073 1,079
1,081 1,121 1,139 1,147 1,157 1,159 1,189 1,207 1,219 1,241
1,247 1,261 1,271 1,273 1,333 1,343 1,349 1,357 1,363 1,369

First brilliant number >= 10^1 is 10 at position 4
First brilliant number >= 10^2 is 121 at position 11
First brilliant number >= 10^3 is 1,003 at position 74
First brilliant number >= 10^4 is 10,201 at position 242
First brilliant number >= 10^5 is 100,013 at position 2,505
First brilliant number >= 10^6 is 1,018,081 at position 10,538
First brilliant number >= 10^7 is 10,000,043 at position 124,364
First brilliant number >= 10^8 is 100,140,049 at position 573,929
First brilliant number >= 10^9 is 1,000,000,081 at position 7,407,841
First brilliant number >= 10^10 is 10,000,600,009 at position 35,547,995
First brilliant number >= 10^11 is 100,000,000,147 at position 491,316,167
First brilliant number >= 10^12 is 1,000,006,000,009 at position 2,409,600,866
First brilliant number >= 10^13 is 10,000,000,000,073 at position 34,896,253,010
First brilliant number >= 10^14 is 100,000,380,000,361 at position 174,155,363,187
First brilliant number >= 10^15 is 1,000,000,000,000,003 at position 2,601,913,448,897

jq

Works with: jq

Also works with gojq and fq

The following implementation has been selected for its combination of conceptual simplicity and speed. It turns out that using `is_prime` is significantly faster than adapting the jq code for `is_semiprime` at Semiprime#jq.

def is_brilliant:
  . as $in
  | sqrt as $sqrt

  | def is_prime:
    . as $n
    | if ($n < 2)         then false
      elif ($n % 2 == 0)  then $n == 2
      elif ($n % 3 == 0)  then $n == 3
      elif ($n % 5 == 0)  then $n == 5
      elif ($n % 7 == 0)  then $n == 7
      elif ($n % 11 == 0) then $n == 11
      elif ($n % 13 == 0) then $n == 13
      elif ($n % 17 == 0) then $n == 17
      elif ($n % 19 == 0) then $n == 19
      else 23
           | until( . > $sqrt or ($n % . == 0); .+2)
  	 | . * . > $n
      end;
  
  {i: 2, n: .}
  | until( (.i > $sqrt) or .result;
      if .n % .i == 0
      then .n /= .i
      | if (.i|tostring|length) == (.n|tostring|length)
        # notice there is no need to check that .i is prime
        and (.n | is_prime)
        then .result = 1
	else .result = 0
        end
      else .i += 1
      end)
  | .result == 1;

# Output a stream of brilliant numbers
def brilliants:
  4,6,9,10,14, (range(15;infinite;2) | select(is_brilliant));

def monitor(generator; $power):
    pow(10; $power) as $power
    | label $out
    | foreach generator as $x ({n: 0, p: -1, watch: 1};
        .n += 1
        | if $x >= .watch
          then .emit = true
          | .watch *= 10 | .p += 1
          | if .watch >= $power then ., break $out else . end
          else .emit = null
          end;
        select(.emit) | [.p, .n, $x]) ;

"The first 100 brilliant numbers:",
[limit(100;  brilliants)],
"\n[power of 10, index, brilliant]",
monitor(brilliants; 7)
Output:
The first 100 brilliant numbers:
[4,6,9,10,14,15,21,25,35,49,121,143,169,187,209,221,247,253,289,299,319,323,341,361,377,391,403,407,437,451,473,481,493,517,527,529,533,551,559,583,589,611,629,649,667,671,689,697,703,713,731,737,767,779,781,793,799,803,817,841,851,869,871,893,899,901,913,923,943,949,961,979,989,1003,1007,1027,1037,1067,1073,1079,1081,1121,1139,1147,1157,1159,1189,1207,1219,1241,1247,1261,1271,1273,1333,1343,1349,1357,1363,1369]

[power of 10, index, brilliant]
[0,1,4]
[1,4,10]
[2,11,121]
[3,74,1003]
[4,242,10201]
[5,2505,100013]
[6,10538,1018081]

Julia

using Primes

function isbrilliant(n)
    p = factor(n).pe
    return (length(p) == 1 && p[1][2] == 2) ||
       length(p) == 2 && ndigits(p[1][1]) == ndigits(p[2][1]) && p[1][2] == p[2][2] == 1
end

function testbrilliants()
    println("First 100 brilliant numbers:")
    foreach(p -> print(lpad(p[2], 5), p[1] % 20 == 0 ? "\n" : ""),
       enumerate(filter(isbrilliant, 1:1370)))
    bcount, results, positions = 0, zeros(Int, 9), zeros(Int, 9)
    for n in 1:10^10
        if isbrilliant(n)
            bcount += 1
            for i in 1:9
                if n >= 10^i && results[i] == 0
                    results[i] = n
                    positions[i] = bcount
                    println("First >=", lpad(10^i, 12), " is", lpad(bcount, 8),
                       " in the series: $n")
                end
            end
        end
    end
    return results, positions
end

testbrilliants()
Output:
First 100 brilliant numbers:
    4    6    9   10   14   15   21   25   35   49  121  143  169  187  209  221  247  253  289  299
  319  323  341  361  377  391  403  407  437  451  473  481  493  517  527  529  533  551  559  583
  589  611  629  649  667  671  689  697  703  713  731  737  767  779  781  793  799  803  817  841
  851  869  871  893  899  901  913  923  943  949  961  979  989 1003 1007 1027 1037 1067 1073 1079
 1081 1121 1139 1147 1157 1159 1189 1207 1219 1241 1247 1261 1271 1273 1333 1343 1349 1357 1363 1369
First >=          10 is       4 in the series: 10
First >=         100 is      11 in the series: 121
First >=        1000 is      74 in the series: 1003
First >=       10000 is     242 in the series: 10201
First >=      100000 is    2505 in the series: 100013
First >=     1000000 is   10538 in the series: 1018081
First >=    10000000 is  124364 in the series: 10000043
First >=   100000000 is  573929 in the series: 100140049
First >=  1000000000 is 7407841 in the series: 1000000081

Mathematica/Wolfram Language

ClearAll[PrimesDecade]
PrimesDecade[n_Integer] := Module[{bounds},
  bounds = {PrimePi[10^n] + 1, PrimePi[10^(n + 1) - 1]};
  Prime[Range @@ bounds]
  ]
ds = Union @@ Table[Union[Times @@@ Tuples[PrimesDecade[d], 2]], {d, 0, 4}];

Multicolumn[Take[ds, 100], {Automatic, 8}, Appearance -> "Horizontal"]

sel = Min /@ GatherBy[Select[ds, GreaterEqualThan[10]], IntegerLength];
Grid[{#, FirstPosition[ds, #][[1]]} & /@ sel]
Output:
4	6	9	10	14	15	21	25
35	49	121	143	169	187	209	221
247	253	289	299	319	323	341	361
377	391	403	407	437	451	473	481
493	517	527	529	533	551	559	583
589	611	629	649	667	671	689	697
703	713	731	737	767	779	781	793
799	803	817	841	851	869	871	893
899	901	913	923	943	949	961	979
989	1003	1007	1027	1037	1067	1073	1079
1081	1121	1139	1147	1157	1159	1189	1207
1219	1241	1247	1261	1271	1273	1333	1343
1349	1357	1363	1369				

10	4
121	11
1003	74
10201	242
100013	2505
1018081	10538
10000043	124364
100140049	573929
1000000081	7407841

Nim

Translation of: C++
Translation of: Java
import std/[algorithm, math, strformat, strutils]

func primes(lim: Natural): seq[Natural] =
  ## Build list of primes using a sieve of Erathostenes.
  var composite = newSeq[bool]((lim + 1) shr 1)
  composite[0] = true
  for n in countup(3, int(sqrt(lim.toFloat)), 2):
    if not composite[n shr 1]:
      for k in countup(n * n, lim, 2 * n):
        composite[k shr 1] = true
  result.add 2
  for n in countup(3, lim, 2):
    if not composite[n shr 1]:
      result.add n

func getPrimesByDigits(lim: Natural): seq[seq[Natural]] =
  ## Distribute primes according to their number of digits.
  var p = 10
  result.add @[]
  for prime in primes(lim):
    if prime > p:
      p *= 10
      if p > 10 * lim: break
      result.add @[]
    result[^1].add prime

let primesByDigits = getPrimesByDigits(10^9-1)

###

echo "First 100 brilliant numbers:"

var brilliantNumbers: seq[Natural]
for primes in primesByDigits:
  for i in 0..primes.high:
    for j in 0..i:
      brilliantNumbers.add primes[i] * primes[j]
  if brilliantNumbers.len >= 100: break
brilliantNumbers.sort()

for i in 0..99:
  stdout.write &"{brilliantNumbers[i]:>5}"
  if i mod 10 == 9: echo()
echo()


###

var power = 10
var count = 0
for p in 1..<(2 * primesByDigits.len):
  let primes = primesByDigits[p shr 1]
  var pos = count + 1
  var minProduct = int.high
  for i, p1 in primes:
    let j = primes.toOpenArray(i, primes.high).lowerBound((power + p1 - 1) div p1)
    let p2 = primes[i + j]
    let product = p1 * p2
    if product < minProduct:
      minProduct = product
    inc pos, j
    if p1 >= p2: break
  echo &"First brilliant number ⩾ 10^{p:<2} is {minProduct} at position {insertSep($pos)}"
  power *= 10
  if p mod 2 == 1:
    inc count, primes.len * (primes.len + 1) div 2
Output:

Most of the execution time is used to fill the sieve of Erathostenes.

First 100 brilliant numbers:
    4    6    9   10   14   15   21   25   35   49
  121  143  169  187  209  221  247  253  289  299
  319  323  341  361  377  391  403  407  437  451
  473  481  493  517  527  529  533  551  559  583
  589  611  629  649  667  671  689  697  703  713
  731  737  767  779  781  793  799  803  817  841
  851  869  871  893  899  901  913  923  943  949
  961  979  989 1003 1007 1027 1037 1067 1073 1079
 1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
 1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First brilliant number ⩾ 10^1  is 10 at position 4
First brilliant number ⩾ 10^2  is 121 at position 11
First brilliant number ⩾ 10^3  is 1003 at position 74
First brilliant number ⩾ 10^4  is 10201 at position 242
First brilliant number ⩾ 10^5  is 100013 at position 2_505
First brilliant number ⩾ 10^6  is 1018081 at position 10_538
First brilliant number ⩾ 10^7  is 10000043 at position 124_364
First brilliant number ⩾ 10^8  is 100140049 at position 573_929
First brilliant number ⩾ 10^9  is 1000000081 at position 7_407_841
First brilliant number ⩾ 10^10 is 10000600009 at position 35_547_995
First brilliant number ⩾ 10^11 is 100000000147 at position 491_316_167
First brilliant number ⩾ 10^12 is 1000006000009 at position 2_409_600_866
First brilliant number ⩾ 10^13 is 10000000000073 at position 34_896_253_010
First brilliant number ⩾ 10^14 is 100000380000361 at position 174_155_363_187
First brilliant number ⩾ 10^15 is 1000000000000003 at position 2_601_913_448_897
First brilliant number ⩾ 10^16 is 10000001400000049 at position 13_163_230_391_313
First brilliant number ⩾ 10^17 is 100000000000000831 at position 201_431_415_980_419

Perl

Library: ntheory
use strict;
use warnings;
use feature 'say';
use List::AllUtils <max head firstidx uniqint>;
use ntheory <primes is_semiprime forsetproduct>;

sub table { my $t = shift() * (my $c = 1 + length max @_); ( sprintf( ('%'.$c.'d')x@_, @_) ) =~ s/.{1,$t}\K/\n/gr }
sub comma { reverse ((reverse shift) =~ s/(.{3})/$1,/gr) =~ s/^,//r }

my(@B,@Br);
for my $oom (1..5) {
    my @P = grep { $oom == length } @{primes(10**$oom)};
    forsetproduct { is_semiprime($_[0] * $_[1]) and push @B, $_[0] * $_[1] } \@P, \@P;
    @Br = uniqint sort { $a <=> $b } @Br, @B;
}

say "First 100 brilliant numbers:\n" . table 10, head 100, @Br;

for my $oom (1..9) {
    my $key = firstidx { $_ > 10**$oom } @Br;
    printf "First >= %13s is position %9s in the series: %13s\n", comma(10**$oom), comma($key), comma $Br[$key];
}
Output:
First 100 brilliant numbers:
    4    6    9   10   14   15   21   25   35   49
  121  143  169  187  209  221  247  253  289  299
  319  323  341  361  377  391  403  407  437  451
  473  481  493  517  527  529  533  551  559  583
  589  611  629  649  667  671  689  697  703  713
  731  737  767  779  781  793  799  803  817  841
  851  869  871  893  899  901  913  923  943  949
  961  979  989 1003 1007 1027 1037 1067 1073 1079
 1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
 1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First >=            10 is position         4 in the series:            14
First >=           100 is position        10 in the series:           121
First >=         1,000 is position        73 in the series:         1,003
First >=        10,000 is position       241 in the series:        10,201
First >=       100,000 is position     2,504 in the series:       100,013
First >=     1,000,000 is position    10,537 in the series:     1,018,081
First >=    10,000,000 is position    124364 in the series:    10,000,043
First >=   100,000,000 is position    573929 in the series:   100,140,049
First >= 1,000,000,000 is position   7407841 in the series: 1,000,000,081

Faster approach

Translation of: Sidef
use 5.020;
use strict;
use warnings;

use ntheory qw(:all);
use experimental qw(signatures);

sub is_briliant_number ($n) {
    is_semiprime($n) || return;
    my @f = factor($n);
    length($f[0]) == length($f[1]);
}

sub next_brilliant_number ($n) {
    ++$n while not is_briliant_number($n);
    $n;
}

sub brilliant_numbers_count ($n) {

    use integer;

    my $count = 0;
    my $len   = length(sqrtint($n));

    foreach my $k (1 .. $len - 1) {
        my $pi = prime_count(10**($k - 1), 10**$k - 1);
        $count += binomial($pi, 2) + $pi;
    }

    my $min = 10**($len - 1);
    my $max = 10**$len - 1;

    my $pi_min = prime_count($min);
    my $pi_max = prime_count($max);

    my $j  = -1;

    forprimes {
        if ($_*$_ <= $n) {
            $count += (($max <= $n/$_) ? $pi_max : prime_count($n/$_)) - $pi_min - ++$j;
        }
        else {
            lastfor;
        }
    } $min, $max;

    return $count;
}

say "First 100 brilliant numbers:";

my @nums;

for (my $k = 1 ; scalar(@nums) < 100 ; ++$k) {
    push(@nums, $k) if is_briliant_number($k);
}

while (@nums) {
    my @slice = splice(@nums, 0, 10);
    say join ' ', map { sprintf("%4s", $_) } @slice;
}

say '';

foreach my $n (1 .. 13) {
    my $v = next_brilliant_number(vecprod((10) x $n));
    printf("First brilliant number >= 10^%d is %s", $n, $v);
    printf(" at position %s\n", brilliant_numbers_count($v));
}
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49
 121  143  169  187  209  221  247  253  289  299
 319  323  341  361  377  391  403  407  437  451
 473  481  493  517  527  529  533  551  559  583
 589  611  629  649  667  671  689  697  703  713
 731  737  767  779  781  793  799  803  817  841
 851  869  871  893  899  901  913  923  943  949
 961  979  989 1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First brilliant number >= 10^1 is 10 at position 4
First brilliant number >= 10^2 is 121 at position 11
First brilliant number >= 10^3 is 1003 at position 74
First brilliant number >= 10^4 is 10201 at position 242
First brilliant number >= 10^5 is 100013 at position 2505
First brilliant number >= 10^6 is 1018081 at position 10538
First brilliant number >= 10^7 is 10000043 at position 124364
First brilliant number >= 10^8 is 100140049 at position 573929
First brilliant number >= 10^9 is 1000000081 at position 7407841
First brilliant number >= 10^10 is 10000600009 at position 35547995
First brilliant number >= 10^11 is 100000000147 at position 491316167
First brilliant number >= 10^12 is 1000006000009 at position 2409600866
First brilliant number >= 10^13 is 10000000000073 at position 34896253010

Phix

Translation of: C++
Library: Phix/online

Replaced with C++ translation; much faster and now goes comfortably to 1e15 even on 32 bit. You can run this online here.

--
-- demo\rosetta\BrilliantNumbers.exw
-- =================================
--
with javascript_semantics
requires("1.0.2") -- (for in)
atom t0 = time()

function get_primes_by_digits(integer limit)
    sequence primes = get_primes_le(power(10,limit)),
             primes_by_digits = {}
    integer p = 10
    while length(primes) do
        integer pi = abs(binary_search(p,primes))-1
        primes_by_digits &= {primes[1..pi]}
        primes = primes[pi+1..$]
        p*= 10
    end while
    return primes_by_digits
end function
sequence primes_by_digits = get_primes_by_digits(8)
 
procedure first100()
    sequence brilliant_numbers = {}
    for primes in primes_by_digits do
        for i=1 to length(primes) do
--see talk page
--          for j=i to length(primes) do 
            for j=1 to i do 
                brilliant_numbers &= primes[i]*primes[j]
            end for
        end for
        if length(brilliant_numbers)>=100 then exit end if
    end for
    brilliant_numbers = sort(brilliant_numbers)[1..100]
    sequence j100 = join_by(brilliant_numbers,1,10," ","\n","%,5d")
    printf(1,"First 100 brilliant numbers:\n%s\n\n",{j100})
end procedure
first100()

atom pwr = 10, count = 0
for p=1 to 2*length(primes_by_digits)-1 do
    sequence primes = primes_by_digits[floor(p/2)+1]
    atom pos = count+1,
         min_product = 0
    for i=1 to length(primes) do
        integer p1 = primes[i],
                j = abs(binary_search(floor((pwr+p1-1)/p1),primes,i))
        if j<=length(primes) then -- (always is, I think)
            integer p2 = primes[j]
            atom prod = p1*p2
            if min_product=0 or prod<min_product then
                min_product = prod
            end if          
            pos += j-i
            if p1>=p2 then exit end if
        end if
    end for
    printf(1,"First brilliant number >= 10^%d is %,d at position %,d\n", {p, min_product, pos})
    pwr *= 10;
    if odd(p) then
        integer size = length(primes)
        count += size * (size + 1) / 2;
    end if
end for
?elapsed(time()-t0)
{} = wait_key()
Output:
First 100 brilliant numbers:
    4     6     9    10    14    15    21    25    35    49
  121   143   169   187   209   221   247   253   289   299
  319   323   341   361   377   391   403   407   437   451
  473   481   493   517   527   529   533   551   559   583
  589   611   629   649   667   671   689   697   703   713
  731   737   767   779   781   793   799   803   817   841
  851   869   871   893   899   901   913   923   943   949
  961   979   989 1,003 1,007 1,027 1,037 1,067 1,073 1,079
1,081 1,121 1,139 1,147 1,157 1,159 1,189 1,207 1,219 1,241
1,247 1,261 1,271 1,273 1,333 1,343 1,349 1,357 1,363 1,369


First brilliant number >= 10^1 is 10 at position 4
First brilliant number >= 10^2 is 121 at position 11
First brilliant number >= 10^3 is 1,003 at position 74
First brilliant number >= 10^4 is 10,201 at position 242
First brilliant number >= 10^5 is 100,013 at position 2,505
First brilliant number >= 10^6 is 1,018,081 at position 10,538
First brilliant number >= 10^7 is 10,000,043 at position 124,364
First brilliant number >= 10^8 is 100,140,049 at position 573,929
First brilliant number >= 10^9 is 1,000,000,081 at position 7,407,841
First brilliant number >= 10^10 is 10,000,600,009 at position 35,547,995
First brilliant number >= 10^11 is 100,000,000,147 at position 491,316,167
First brilliant number >= 10^12 is 1,000,006,000,009 at position 2,409,600,866
First brilliant number >= 10^13 is 10,000,000,000,073 at position 34,896,253,010
First brilliant number >= 10^14 is 100,000,380,000,361 at position 174,155,363,187
First brilliant number >= 10^15 is 1,000,000,000,000,003 at position 2,601,913,448,897
"3.3s"

Prolog

works with swi-prolog

factors(N, Flist):-
	factors(N, 2, 0, Flist).

factors(1, _, _, []).
factors(_, _, Cnt, []):- Cnt > 1,!.
factors(N, Start, Cnt, [Fac|FList]):-
	N1 is floor(sqrt(N)),
	between(Start, N1, Fac),
	N mod Fac =:= 0,!,
	N2 is N div Fac,
	Cnt1 is Cnt + 1,
	factors(N2, Fac, Cnt1, FList).
factors(N, _, _, [N]):- N >= 2.

brilliantList(Start, Limit, List):-
	findall(N, brilliants(Start, Limit, N), List).
nextBrilliant(Start, N):-
	brilliants(Start, inf, N).
isBrilliant(N):-
	brilliants(2, inf, N).
brilliants(Start, Limit, N):-
	between(Start, Limit, N),
	factors(N,[F1,F2]),
	F1 * F2 =:= N,
	digits(F1, D1), digits(F2, D2),
	D1 =:= D2.

digits(N, D):-
	D is 1 + floor(log10(N)).

%% generate results

run(LimitList):-
	run(LimitList, 0, 2).
run([], _, _).
run([Limit|LList], OldCount, OldLimit):-
	Limit1 is Limit - 1,
	statistics(runtime,[Start|_]),
	brilliantList(OldLimit, Limit1, BList),
	length(BList, Cnt),
	Cnt1 is OldCount + Cnt,
	Index is Cnt1 + 1,
	nextBrilliant(Limit, Bril),!,
	statistics(runtime,[Stop|_]),
	Time is Stop - Start,
	writef('first >=%8r is%8r at position%6r [time:%6r]', [Limit, Bril, Index, Time]),nl,	
	run(LList, Cnt1, Limit).

showList(List, Limit):-
	findnsols(Limit, X, (member(X, List), writef('%5r', [X])), _),
	nl, fail.
showList(_, _).
	
do:-findnsols(100, B, isBrilliant(B), BList),!,
	showList(BList, 10),nl,
	findall(N, (between(1, 6, X), N is 10^X), LimitList),
	run(LimitList).
Output:
?- do.
    4    6    9   10   14   15   21   25   35   49
  121  143  169  187  209  221  247  253  289  299
  319  323  341  361  377  391  403  407  437  451
  473  481  493  517  527  529  533  551  559  583
  589  611  629  649  667  671  689  697  703  713
  731  737  767  779  781  793  799  803  817  841
  851  869  871  893  899  901  913  923  943  949
  961  979  989 1003 1007 1027 1037 1067 1073 1079
 1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
 1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

first >=      10 is      10 at position     4 [time:     0]
first >=     100 is     121 at position    11 [time:     1]
first >=    1000 is    1003 at position    74 [time:     4]
first >=   10000 is   10201 at position   242 [time:    73]
first >=  100000 is  100013 at position  2505 [time:  1442]
first >= 1000000 is 1018081 at position 10538 [time: 34054]
true.

Python

Using primesieve and numpy modules. If program is run to above 1018 it overflows 64 bit integers (that's what primesieve module backend uses internally).

from primesieve.numpy import primes
from math import isqrt
import numpy as np

max_order = 9
blocks = [primes(10**n, 10**(n + 1)) for n in range(max_order)]

def smallest_brilliant(lb):
    pos = 1
    root = isqrt(lb)

    for blk in blocks:
        n = len(blk)
        if blk[-1]*blk[-1] < lb:
            pos += n*(n + 1)//2
            continue

        i = np.searchsorted(blk, root, 'left')
        i += blk[i]*blk[i] < lb

        if not i:
            return blk[0]*blk[0], pos

        p = blk[:i + 1]
        q = (lb - 1)//p
        idx = np.searchsorted(blk, q, 'right')

        sel = idx < n
        p, idx = p[sel], idx[sel]
        q = blk[idx]

        sel = q >= p
        p, q, idx = p[sel], q[sel], idx[sel]

        pos += np.sum(idx - np.arange(len(idx)))
        return np.min(p*q), pos

res = []
p = 0
for i in range(100):
    p, _ = smallest_brilliant(p + 1)
    res.append(p)

print(f'first 100 are {res}')

for i in range(max_order*2):
    thresh = 10**i
    p, pos = smallest_brilliant(thresh)
    print(f'Above 10^{i:2d}: {p:20d} at #{pos}')
Output:
first 100 are [4, 6, 9, 10, 14, 15, 21, 25, 35, 49, 121, 143, 169, 187, 209, 221, 247, 253, 289, 299, 319, 323, 341, 361, 377, 391, 403, 407, 437, 451, 473, 481, 493, 517, 527, 529, 533, 551, 559, 583, 589, 611, 629, 649, 667, 671, 689, 697, 703, 713, 731, 737, 767, 779, 781, 793, 799, 803, 817, 841, 851, 869, 871, 893, 899, 901, 913, 923, 943, 949, 961, 979, 989, 1003, 1007, 1027, 1037, 1067, 1073, 1079, 1081, 1121, 1139, 1147, 1157, 1159, 1189, 1207, 1219, 1241, 1247, 1261, 1271, 1273, 1333, 1343, 1349, 1357, 1363, 1369]
Above 10^ 0:                    4 at #1
Above 10^ 1:                   10 at #4
Above 10^ 2:                  121 at #11
Above 10^ 3:                 1003 at #74
Above 10^ 4:                10201 at #242
Above 10^ 5:               100013 at #2505
Above 10^ 6:              1018081 at #10538
Above 10^ 7:             10000043 at #124364
Above 10^ 8:            100140049 at #573929
Above 10^ 9:           1000000081 at #7407841
Above 10^10:          10000600009 at #35547995
Above 10^11:         100000000147 at #491316167
Above 10^12:        1000006000009 at #2409600866
Above 10^13:       10000000000073 at #34896253010
Above 10^14:      100000380000361 at #174155363187
Above 10^15:     1000000000000003 at #2601913448897
Above 10^16:    10000001400000049 at #13163230391313
Above 10^17:   100000000000000831 at #201431415980419

Quackery

eratosthenes and isprime are defined at Sieve of Eratosthenes#Quackery.

bsearchwith is defined at Binary search#Quackery.

  [ 1 swap
    [ 10 / dup iff
        [ dip 1+ ]
      else done
      again ]
    drop ]                 is digits (   n --> n )

  [ over size 0 swap 2swap
    bsearchwith < drop ]   is search ( [ n --> n )

  1010 eratosthenes
  1 temp put
  [] [] []
  1010 times
    [ i^ isprime if
      [ temp share
        i^ digits < if
          [ nested join
            []
            1 temp tally ]
        i^ join ] ]
  nested join
  temp release
  witheach
    [ dup witheach
        [ over witheach
          [ over *
            dip rot join unrot ]
          drop behead drop ]
      drop ]
  sort
  say "First 100 brilliant numbers:" cr
  dup 100 split drop
  unbuild
  2 split nip -2 split drop
  nest$ 40 wrap$ cr cr
  6 times
    [ dup dup 10 i^ 1+ **
      say "First > "
      dup 1 - echo
      say " is "
      search tuck peek echo
      say " at position " 1+ echo
      say "." cr ]
  drop
Output:
First 100 brilliant numbers:

4 6 9 10 14 15 21 25 35 49 121 143 169
187 209 221 247 253 289 299 319 323 341
361 377 391 403 407 437 451 473 481 493
517 527 529 533 551 559 583 589 611 629
649 667 671 689 697 703 713 731 737 767
779 781 793 799 803 817 841 851 869 871
893 899 901 913 923 943 949 961 979 989
1003 1007 1027 1037 1067 1073 1079 1081
1121 1139 1147 1157 1159 1189 1207 1219
1241 1247 1261 1271 1273 1333 1343 1349
1357 1363 1369

First > 9 is 10 at position 4.
First > 99 is 121 at position 11.
First > 999 is 1003 at position 74.
First > 9999 is 10201 at position 242.
First > 99999 is 100013 at position 2505.
First > 999999 is 1018081 at position 10538.


Raku

1 through 7 are fast. 8 and 9 take a bit longer.

use Lingua::EN::Numbers;

# Find an abundance of primes to use to generate brilliants
my %primes = (2..100000).grep( &is-prime ).categorize: { .chars };

# Generate brilliant numbers
my @brilliant = lazy flat (1..*).map: -> $digits {
    sort flat (^%primes{$digits}).race.map: { %primes{$digits}[$_] X× (flat %primes{$digits}[$_ .. *]) }
};

# The task
put "First 100 brilliant numbers:\n" ~ @brilliant[^100].batch(10)».fmt("%4d").join("\n") ~ "\n" ;

for 1 .. 7 -> $oom {
    my $threshold = exp $oom, 10;
    my $key = @brilliant.first: :k, * >= $threshold;
    printf "First >= %13s is %9s in the series: %13s\n", comma($threshold), ordinal-digit(1 + $key, :u), comma @brilliant[$key];
}
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49
 121  143  169  187  209  221  247  253  289  299
 319  323  341  361  377  391  403  407  437  451
 473  481  493  517  527  529  533  551  559  583
 589  611  629  649  667  671  689  697  703  713
 731  737  767  779  781  793  799  803  817  841
 851  869  871  893  899  901  913  923  943  949
 961  979  989 1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First >=            10 is       4ᵗʰ in the series:            10
First >=           100 is      11ᵗʰ in the series:           121
First >=         1,000 is      74ᵗʰ in the series:         1,003
First >=        10,000 is     242ⁿᵈ in the series:        10,201
First >=       100,000 is    2505ᵗʰ in the series:       100,013
First >=     1,000,000 is   10538ᵗʰ in the series:     1,018,081
First >=    10,000,000 is  124364ᵗʰ in the series:    10,000,043
First >=   100,000,000 is  573929ᵗʰ in the series:   100,140,049
First >= 1,000,000,000 is 7407841ˢᵗ in the series: 1,000,000,081

RPL

A fast semiprime checker was needed here.

RPL code Comment
 ≪ DUP √ CEIL 0 → max div
   ≪ 0 
      2 max FOR j 
         WHILE OVER j MOD NOT REPEAT
            SWAP j / SWAP 1 + j 'div' STO END
         IF DUP 2 > THEN max 'j' STO END
      NEXT 
      IF OVER 1 > THEN 1 + END
      SWAP DROP 2 == div *
≫ ≫ ‘SPR1?’ STO

≪ 
  IF DUP SPR1? DUP THEN
      DUP2 / XPON SWAP XPON == SWAP DROP 
  ELSE DROP2 0 END
≫ ‘BRIL?’ STO 
SPR1? ( n → divisor ) 
cnt = 0;
for j = 2 to ceiling(sqrt(n))
   while (n % j == 0)
        n /= j ; ++cnt ; div = j ; 
   if cnt > 2 then break;
 
if (num > 1) ++cnt;
return divisor if semiprime, otherwise zero


BRIL? ( n → boolean ) 
if semiprime then
   compare divisors' size
else not a brilliant number

≪ { } 1 WHILE OVER SIZE 100 < REPEAT IF DUP BRIL? THEN SWAP OVER + SWAP END 1 + END DROP ≫ EVAL
≪ { } 1 5 FOR n n ALOG WHILE DUP BRIL? NOT REPEAT 1 + END + NEXT ≫ EVAL
Output:
2: { 4 6 9 10 14 15 21 25 35 49 121 143 169 187 209 221 247 253 289 299 319 323 341 361 377 391 403 407 437 451 473 481 493 517 527 529 533 551 559 583 589 611 629 649 667 671 689 697 703 713 731 737 767 779 781 793 799 803 817 841 851 869 871 893 899 901 913 923 943 949 961 979 989 1003 1007 1027 1037 1067 1073 1079 1081 1121 1139 1147 1157 1159 1189 1207 1219 1241 1247 1261 1271 1273 1333 1343 1349 1357 1363 1369 }
1: { 10 121 1003 10201 100013 1018081 }

Rust

Translation of: C++
// [dependencies]
// primal = "0.3"
// indexing = "0.4.1"

fn get_primes_by_digits(limit: usize) -> Vec<Vec<usize>> {
    let mut primes_by_digits = Vec::new();
    let mut power = 10;
    let mut primes = Vec::new();
    for prime in primal::Primes::all().take_while(|p| *p < limit) {
        if prime > power {
            primes_by_digits.push(primes);
            primes = Vec::new();
            power *= 10;
        }
        primes.push(prime);
    }
    primes_by_digits.push(primes);
    primes_by_digits
}

fn main() {
    use indexing::algorithms::lower_bound;
    use std::time::Instant;

    let start = Instant::now();

    let primes_by_digits = get_primes_by_digits(1000000000);

    println!("First 100 brilliant numbers:");
    let mut brilliant_numbers = Vec::new();
    for primes in &primes_by_digits {
        for i in 0..primes.len() {
            let p1 = primes[i];
            for j in i..primes.len() {
                let p2 = primes[j];
                brilliant_numbers.push(p1 * p2);
            }
        }
        if brilliant_numbers.len() >= 100 {
            break;
        }
    }
    brilliant_numbers.sort();
    for i in 0..100 {
        let n = brilliant_numbers[i];
        print!("{:4}{}", n, if (i + 1) % 10 == 0 { '\n' } else { ' ' });
    }

    println!();
    let mut power = 10;
    let mut count = 0;
    for p in 1..2 * primes_by_digits.len() {
        let primes = &primes_by_digits[p / 2];
        let mut position = count + 1;
        let mut min_product = 0;
        for i in 0..primes.len() {
            let p1 = primes[i];
            let n = (power + p1 - 1) / p1;
            let j = lower_bound(&primes[i..], &n);
            let p2 = primes[i + j];
            let product = p1 * p2;
            if min_product == 0 || product < min_product {
                min_product = product;
            }
            position += j;
            if p1 >= p2 {
                break;
            }
        }
        println!("First brilliant number >= 10^{p} is {min_product} at position {position}");
        power *= 10;
        if p % 2 == 1 {
            let size = primes.len();
            count += size * (size + 1) / 2;
        }
    }

    let time = start.elapsed();
    println!("\nElapsed time: {} milliseconds", time.as_millis());
}
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49
 121  143  169  187  209  221  247  253  289  299
 319  323  341  361  377  391  403  407  437  451
 473  481  493  517  527  529  533  551  559  583
 589  611  629  649  667  671  689  697  703  713
 731  737  767  779  781  793  799  803  817  841
 851  869  871  893  899  901  913  923  943  949
 961  979  989 1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First brilliant number >= 10^1 is 10 at position 4
First brilliant number >= 10^2 is 121 at position 11
First brilliant number >= 10^3 is 1003 at position 74
First brilliant number >= 10^4 is 10201 at position 242
First brilliant number >= 10^5 is 100013 at position 2505
First brilliant number >= 10^6 is 1018081 at position 10538
First brilliant number >= 10^7 is 10000043 at position 124364
First brilliant number >= 10^8 is 100140049 at position 573929
First brilliant number >= 10^9 is 1000000081 at position 7407841
First brilliant number >= 10^10 is 10000600009 at position 35547995
First brilliant number >= 10^11 is 100000000147 at position 491316167
First brilliant number >= 10^12 is 1000006000009 at position 2409600866
First brilliant number >= 10^13 is 10000000000073 at position 34896253010
First brilliant number >= 10^14 is 100000380000361 at position 174155363187
First brilliant number >= 10^15 is 1000000000000003 at position 2601913448897
First brilliant number >= 10^16 is 10000001400000049 at position 13163230391313
First brilliant number >= 10^17 is 100000000000000831 at position 201431415980419

Elapsed time: 1515 milliseconds

Scala

val primes = 2 #:: LazyList.from(3, 2)    // simple prime
    .filter(p => (3 to math.sqrt(p).ceil.toInt by 2).forall(p % _ > 0))

def brilliantSemiPrimes(limit: Int): Seq[Int] = {
  def iter(primeList: LazyList[Int], bLimit: Int, acc: Seq[Int]): Seq[Int] = {
    val (start, tail) = (primeList.head, primeList.tail)
    val brilliants = primeList
            .takeWhile(_ <= bLimit)
            .map(_ * start)
            .takeWhile(_ <= limit)
    if (brilliants.isEmpty) return acc
    val bLimit1 = if (tail.head > bLimit) 10 * bLimit else bLimit
    iter(tail, bLimit1, brilliants.toSeq ++ acc)
  }
  iter(primes, 10, Seq()).sorted
}

@main def main = {
  val start = System.currentTimeMillis
  val brList = brilliantSemiPrimes(1500).take(100)
  val duration = System.currentTimeMillis - start
  for (group <- brList.grouped(20))
      println(group.map("%4d".format(_)).mkString(" "))
  println(s"time elapsed: $duration ms\n")

  for (limit <- (1 to 6).map(math.pow(10,_).toInt)) {
    val start = System.currentTimeMillis
    val (bril, index) = brilliantSemiPrimes((limit * 1.25).toInt)
            .zipWithIndex
            .dropWhile((b, _i) => b < limit)
            .head
    val duration = System.currentTimeMillis - start
    println(f"first >= $limit%7d is $bril%7d at position ${index+1}%5d [time(ms) $duration%2d]")
  }
}
Output:
   4    6    9   10   14   15   21   25   35   49  121  143  169  187  209  221  247  253  289  299
 319  323  341  361  377  391  403  407  437  451  473  481  493  517  527  529  533  551  559  583
 589  611  629  649  667  671  689  697  703  713  731  737  767  779  781  793  799  803  817  841
 851  869  871  893  899  901  913  923  943  949  961  979  989 1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241 1247 1261 1271 1273 1333 1343 1349 1357 1363 1369
time elapsed: 3 ms

first >=      10 is      10 at position     4 [time(ms)  1]
first >=     100 is     121 at position    11 [time(ms)  0]
first >=    1000 is    1003 at position    74 [time(ms)  1]
first >=   10000 is   10201 at position   242 [time(ms)  2]
first >=  100000 is  100013 at position  2505 [time(ms)  6]
first >= 1000000 is 1018081 at position 10538 [time(ms) 11]

Sidef

func is_briliant_number(n) {
    n.is_semiprime && (n.factor.map{.len}.uniq.len == 1)
}

func brilliant_numbers_count(n) {

    var count = 0
    var len = n.isqrt.len

    for k in (1 .. len-1) {
        var pi = prime_count(10**(k-1), 10**k - 1)
        count += binomial(pi, 2)+pi
    }

    var min = (10**(len - 1))
    var max = (10**len - 1)

    each_prime(min, max, {|p|
        count += prime_count(p, max `min` idiv(n, p))
    })

    return count
}

say "First 100 brilliant numbers:"

100.by(is_briliant_number).each_slice(10, {|*a|
    say a.map { '%4s' % _}.join(' ')
})

say ''

for n in (1 .. 12) {
    var v = (10**n .. Inf -> first_by(is_briliant_number))
    printf("First brilliant number >= 10^%d is %s", n, v)
    printf(" at position %s\n", brilliant_numbers_count(v))
}
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49
 121  143  169  187  209  221  247  253  289  299
 319  323  341  361  377  391  403  407  437  451
 473  481  493  517  527  529  533  551  559  583
 589  611  629  649  667  671  689  697  703  713
 731  737  767  779  781  793  799  803  817  841
 851  869  871  893  899  901  913  923  943  949
 961  979  989 1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First brilliant number >= 10^1 is 10 at position 4
First brilliant number >= 10^2 is 121 at position 11
First brilliant number >= 10^3 is 1003 at position 74
First brilliant number >= 10^4 is 10201 at position 242
First brilliant number >= 10^5 is 100013 at position 2505
First brilliant number >= 10^6 is 1018081 at position 10538
First brilliant number >= 10^7 is 10000043 at position 124364
First brilliant number >= 10^8 is 100140049 at position 573929
First brilliant number >= 10^9 is 1000000081 at position 7407841
First brilliant number >= 10^10 is 10000600009 at position 35547995
First brilliant number >= 10^11 is 100000000147 at position 491316167
First brilliant number >= 10^12 is 1000006000009 at position 2409600866

Swift

Magnitudes of 1 to 3 is decent, 4 and beyond becomes slow.

// Refs:
// https://www.geeksforgeeks.org/sieve-of-eratosthenes/?ref=leftbar-rightbar
// https://developer.apple.com/documentation/swift/array/init(repeating:count:)-5zvh4
// https://www.geeksforgeeks.org/brilliant-numbers/#:~:text=Brilliant%20Number%20is%20a%20number,25%2C%2035%2C%2049%E2%80%A6.

// Using Sieve of Eratosthenes
func primeArray(n: Int) -> [Bool] {
    
    var primeArr = [Bool](repeating: true, count: n + 1)
    primeArr[0] = false // setting zero to be not prime
    primeArr[1] = false // setting one to be not prime
    
    // finding all primes which are divisible by p and are greater than or equal to the square of it
    var p = 2
    while (p * p) <= n {
        if primeArr[p] == true {
            for j in stride(from: p * 2, through: n, by: p) {
                primeArr[j] = false
            }
        }
        p += 1
    }
    
    return primeArr
}


func digitsCount(n: Int) -> Int {
    // count number of digits for a number
    // increase the count if n divide by 10 is not equal to zero
    var num = n
    var count = 0;
    while num != 0 {
        num = num/10
        count += 1
    }
    
    return count
}


func isBrilliant(n: Int) -> Bool {
    // Set the prime array
    var isPrime = [Bool]()
    isPrime = primeArray(n: n)
    
    // Check if the number is the product of two prime numbers
    // Also check if the digit counts of those prime numbers are the same.
    for i in stride(from: 2, through: n, by: 1) { // i=2, n=50
        let x = n / i // i=2, n=50, x=25
        if (isPrime[i] && isPrime[x] && x * i == n) { // i=2, x=50, false
            if (digitsCount(n: i) == digitsCount(n: x)) {
                return true
            }
        }
    }
    
    return false
}


func print100Brilliants() {
    // Print the first 100 brilliant numbers
    var brilNums = [Int]()
    var count = 4
    while brilNums.count != 100 {
        if isBrilliant(n: count) {
            brilNums.append(count)
        }
        count += 1
    }
    
    print("First 100 brilliant numbers:\n", brilNums)
}


func printBrilliantsOfMagnitude() {
    // Print the brilliant numbers of base 10 up to magnitude of 6
    // Including their positions in the array.
    var basePower = 10.0
    var brilNums: [Double] = [0.0]
    var count = 1.0
    while basePower != pow(basePower, 6) {
        if isBrilliant(n: Int(count)) {
            brilNums.append(count)
            if count >= basePower {
                print("First brilliant number >= \(Int(basePower)): \(Int(count)) at position \(brilNums.firstIndex(of: count)!)")
                basePower *= 10
            }
        }
        count += 1
    }
}


print100Brilliants()
printBrilliantsOfMagnitude()
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49 
 121  143  169  187  209  221  247  253  289  299 
 319  323  341  361  377  391  403  407  437  451 
 473  481  493  517  527  529  533  551  559  583 
 589  611  629  649  667  671  689  697  703  713 
 731  737  767  779  781  793  799  803  817  841 
 851  869  871  893  899  901  913  923  943  949 
 961  979  989 1003 1007 1027 1037 1067 1073 1079 
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241 
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369 

First brilliant number >= 10: 10 at position 4 
First brilliant number >= 100: 121 at position 11 
First brilliant number >= 1000: 1003 at position 74 
First brilliant number >= 10000: 10201 at position 242 
First brilliant number >= 100000: 100013 at position 2505 
First brilliant number >= 1000000: 1018081 at position 10538

Wren

Library: Wren-math
Library: Wren-fmt
import "./math" for Int
import "./fmt" for Fmt

var primes = Int.primeSieve(1e7-1)

var getBrilliant = Fn.new { |digits, limit, countOnly|
    var brilliant = []
    var count = 0
    var pow = 1
    var next = Num.maxSafeInteger
    for (k in 1..digits) {
        var s = primes.where { |p| p > pow && p < pow * 10 }.toList
        for (i in 0...s.count) {
            for (j in i...s.count) {
                var prod = s[i] * s[j]
                if (prod < limit) {
                    if (countOnly) {
                        count = count + 1
                    } else {
                        brilliant.add(prod)
                    }
                } else {
                    next = next.min(prod)
                    break
                }
            }
        }
        pow = pow * 10
    }
    return countOnly ? [count, next] : [brilliant, next]
}

System.print("First 100 brilliant numbers:")
var brilliant = getBrilliant.call(2, 10000, false)[0]
brilliant.sort()
brilliant = brilliant[0..99]
Fmt.tprint("$4d", brilliant, 10)
System.print()
for (k in 1..12) {
    var limit = 10.pow(k)
    var res = getBrilliant.call(k, limit, true)
    var total = res[0]
    var next = res[1]
    Fmt.print("First >= $,17d is $,15r in the series: $,17d", limit, total + 1, next)
}
Output:
First 100 brilliant numbers:
   4    6    9   10   14   15   21   25   35   49
 121  143  169  187  209  221  247  253  289  299
 319  323  341  361  377  391  403  407  437  451
 473  481  493  517  527  529  533  551  559  583
 589  611  629  649  667  671  689  697  703  713
 731  737  767  779  781  793  799  803  817  841
 851  869  871  893  899  901  913  923  943  949
 961  979  989 1003 1007 1027 1037 1067 1073 1079
1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First >=                10 is             4th in the series:                10
First >=               100 is            11th in the series:               121
First >=             1,000 is            74th in the series:             1,003
First >=            10,000 is           242nd in the series:            10,201
First >=           100,000 is         2,505th in the series:           100,013
First >=         1,000,000 is        10,538th in the series:         1,018,081
First >=        10,000,000 is       124,364th in the series:        10,000,043
First >=       100,000,000 is       573,929th in the series:       100,140,049
First >=     1,000,000,000 is     7,407,841st in the series:     1,000,000,081
First >=    10,000,000,000 is    35,547,995th in the series:    10,000,600,009
First >=   100,000,000,000 is   491,316,167th in the series:   100,000,000,147
First >= 1,000,000,000,000 is 2,409,600,866th in the series: 1,000,006,000,009

XPL0

func NumDigits(N);      \Return number of digits in N
int  N, Cnt;
[Cnt:= 0;
repeat  N:= N/10;
        Cnt:= Cnt+1;
until   N = 0;
return Cnt;
];

func Brilliant(N);      \Return 'true' if N is a brilliant number
int  N, Limit, Cnt, F;
int  A(3);
[Limit:= sqrt(N);
Cnt:= 0;  F:= 2;
loop    [if rem(N/F) = 0 then
                [A(Cnt):= F;
                Cnt:= Cnt+1;
                if Cnt > 2 then quit;
                N:= N/F;
                ]
        else    F:= F+1;
        if F > N then quit;
        if F > Limit then
                [A(Cnt):= N;
                Cnt:= Cnt+1;
                quit;
                ];
        ];
if Cnt # 2 then return false;
return NumDigits(A(0)) = NumDigits(A(1));
];

int Cnt, N, Mag;
[Format(5, 0);
Cnt:= 0;  N:= 4;
loop    [if Brilliant(N) then
            [RlOut(0, float(N));
            Cnt:= Cnt+1;
            if Cnt >= 100 then quit;
            if rem(Cnt/10) = 0 then CrLf(0);
            ];
        N:= N+1;
        ];
CrLf(0);  CrLf(0);
Format(7, 0);
Cnt:= 0;  N:= 4;  Mag:= 10;
loop    [if Brilliant(N) then
            [Cnt:= Cnt+1;
            if N >= Mag then
                [Text(0, "First >= ");
                RlOut(0, float(Mag));
                Text(0, " is ");
                RlOut(0, float(Cnt));
                Text(0, " in series: ");
                RlOut(0, float(N));
                CrLf(0);
                if Mag >= 1_000_000 then quit;
                Mag:= Mag*10;
                ];
            ];
        N:= N+1;
        ];
]
Output:
    4    6    9   10   14   15   21   25   35   49
  121  143  169  187  209  221  247  253  289  299
  319  323  341  361  377  391  403  407  437  451
  473  481  493  517  527  529  533  551  559  583
  589  611  629  649  667  671  689  697  703  713
  731  737  767  779  781  793  799  803  817  841
  851  869  871  893  899  901  913  923  943  949
  961  979  989 1003 1007 1027 1037 1067 1073 1079
 1081 1121 1139 1147 1157 1159 1189 1207 1219 1241
 1247 1261 1271 1273 1333 1343 1349 1357 1363 1369

First >=      10 is       4 in series:      10
First >=     100 is      11 in series:     121
First >=    1000 is      74 in series:    1003
First >=   10000 is     242 in series:   10201
First >=  100000 is    2505 in series:  100013
First >= 1000000 is   10538 in series: 1018081