g | x | w | all
Bytes Lang Time Link
814Zig250214T134501Z138 Aspe
194Elixir250214T071027Z138 Aspe
041Haskell + hgl240720T020113ZWheat Wi
nanC89230801T055849ZPignotto
nanRust230729T140042Zmousetai
047JavaScript Node.js230801T035122Zl4m2
108Ruby230731T013302ZValue In
053Perl 5230730T232032Zgood old
008Nekomata + e230731T072004Zalephalp
026Charcoal230730T140337ZNeil
073Haskell230730T123742ZGurkengl
059JavaScript Node.js230730T075502Ztsh
095JavaScript230729T144258Zbsoelch
144Retina 0.8.2230730T075426ZNeil
139Haskell230730T070715ZLeopardS
nanScala230729T153542Z138 Aspe
013Jelly230729T164305ZJonathan

Zig, 814 bytes

814 bytes, it can be golfed much more.


Golfed version. Attempt This Online!

const P = struct { f: i32, s: i32 };
fn f(a: []const i8, b: []const i8) bool {
    const A=std.heap.page_allocator;
    var s=std.ArrayList(P).init(A);
    defer s.deinit();
    s.append(P{.f=0,.s=0}) catch unreachable;
    for (0..a.len)|i| {
        var n= std.ArrayList(P).init(A);
        for (s.items)|p|{
            const x1=p.f+a[i];
            const y1=p.s+b[i];
            const x2=p.f+b[i];
            const y2=p.s+a[i];
            if(x1>=0 and y1>=0){
                n.append(.{.f=x1, .s=y1}) catch unreachable;
            }
            if (x2>=0 and y2>=0) {
                n.append(.{ .f=x2, .s=y2}) catch unreachable;
            }
        }
        s.deinit();
        s=n;
    }
    for (s.items)|p|{
        if (p.f==0 and p.s==0){
            return 1<2;
        }
    }
    return 1>2;
}

Ungolfed version. Attempt This Online!

const std = @import("std");

const TestCase = struct {
    a: []const i8,
    b: []const i8,
    expected: bool,
};

// Define a Pair struct
const Pair = struct {
    first: i32,
    second: i32,
};

fn f(a: []const i8, b: []const i8) bool {
    const alloc = std.heap.page_allocator;

    var state = std.ArrayList(Pair).init(alloc);
    defer state.deinit();  // We'll only deinit `state` at the very end

    // Start state with (0, 0)
    _ = state.append(Pair{ .first = 0, .second = 0 }) catch unreachable;

    // Iterate over elements in `a` (and `b`)
    // Prefer "for (a) |_, i|" or "for (0..a.len) |i|" in modern Zig.
    for (0..a.len) |i| {
        var nextState = std.ArrayList(Pair).init(alloc);
        // IMPORTANT: do NOT `defer nextState.deinit();` here, since we'll move it into `state`.

        for (state.items) |pair| {
            const x1 = pair.first + a[i];
            const y1 = pair.second + b[i];
            const x2 = pair.first + b[i];
            const y2 = pair.second + a[i];

            if (x1 >= 0 and y1 >= 0) {
                _ = nextState.append(.{ .first = x1, .second = y1 }) catch unreachable;
            }
            if (x2 >= 0 and y2 >= 0) {
                _ = nextState.append(.{ .first = x2, .second = y2 }) catch unreachable;
            }
        }

        // Deinit old `state`
        state.deinit();

        // Transfer ownership from `nextState` to `state`
        state = nextState;
    }

    // Finally, check if (0,0) is in the final `state`
    for (state.items) |pair| {
        if (pair.first == 0 and pair.second == 0) {
            return true;
        }
    }

    return false;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // Each test case has slices a and b, and the expected boolean value
    const testCases = [_]TestCase{
        .{ .a = &[_]i8{1}, .b = &[_]i8{-1}, .expected = false },
        .{ .a = &[_]i8{ 1, -1 }, .b = &[_]i8{ 1, -1 }, .expected = true },
        .{ .a = &[_]i8{ 1, 1, 1, -1, -1, -1 }, .b = &[_]i8{ 1, -1, 1, -1, 1, -1 }, .expected = true },
        .{ .a = &[_]i8{ 1, -1, 1, -1, -1, -1 }, .b = &[_]i8{ 1, 1, 1, -1, 1, -1 }, .expected = true },
        .{ .a = &[_]i8{ -1, -1, -1, 1, 1, 1 }, .b = &[_]i8{ 1, 1, 1, -1, -1, -1 }, .expected = false },
        .{ .a = &[_]i8{ -1, -1, -1, -1, -1, -1, -1 }, .b = &[_]i8{ -1, -1, -1, -1, -1, -1 }, .expected = false },
        .{ .a = &[_]i8{ -1, -1, -1, -1, -1, -1, -1 }, .b = &[_]i8{ -1, -1, -1, -1, -1, -1, -1 }, .expected = false },
        .{ .a = &[_]i8{ 1, -1, -1, -1, -1, -1, -1 }, .b = &[_]i8{ 1, 1, 1, 1, 1, 1, -1 }, .expected = false },
    };

    // Run each test case
    for (testCases) |testCase| {
        const result = f(testCase.a, testCase.b);

        // Print arrays a
        try stdout.print("a: {{ ", .{});
        for (testCase.a, 0..) |val, idx| {
            try stdout.print("{any}{any}", .{ val, if (idx < testCase.a.len - 1) ", " else "" });
        }
        try stdout.print(" }} ", .{});

        // Print arrays b
        try stdout.print("b: {{ ", .{});
        for (testCase.b, 0..) |val, idx| {
            try stdout.print("{any}{any}", .{ val, if (idx < testCase.b.len - 1) ", " else "" });
        }
        try stdout.print(" }} ", .{});

        try stdout.print("expected: {any}, got: {any}\n", .{ testCase.expected, result });
    }
}

Elixir, 194 bytes

Golfed version. Attempt This Online!

def f(a,b),do: Enum.zip(a,b)|>Enum.reduce([{0,0}],fn{x,y},d->d|>Enum.flat_map(fn{p,q}->[{p+x,q+y},{p+y,q+x}]end)|>Enum.filter(fn{x,y}->x>=0 and y>=0 end)end)|>Enum.any?(fn{x,y}->{x,y}=={0,0}end)

Ungolfed version. Attempt This Online!

defmodule Main do
  # This function corresponds to 'f' in the Scala code.
  # It takes two lists of integers (originally bytes in Scala)
  # and returns a boolean.
  def f(a, b) do
    # Start with the initial accumulator of [{0, 0}]
    # Then for each pair {x, y}, expand each accumulator entry {p, q}
    # into two possibilities, filtering out negative coordinates.
    # Finally check if we have (0, 0) in the resulting list.
    Enum.zip(a, b)
    |> Enum.reduce([{0, 0}], fn {x, y}, acc ->
      acc
      |> Enum.flat_map(fn {p, q} ->
        [
          {p + x, q + y},
          {p + y, q + x}
        ]
      end)
      |> Enum.filter(fn {px, py} ->
        px >= 0 and py >= 0
      end)
    end)
    |> Enum.any?(fn {px, py} -> {px, py} == {0, 0} end)
  end

  # For demonstration, let's mimic the original test cases
  def run_tests do
    test_cases = [
      {[1], [-1], false},
      {[1, -1], [1, -1], true},
      {[1, 1, 1, -1, -1, -1], [1, -1, 1, -1, 1, -1], true},
      {[1, -1, 1, -1, -1, -1], [1, 1, 1, -1, 1, -1], true},
      {[-1, -1, -1, 1, 1, 1], [1, 1, 1, -1, -1, -1], false},
      # Equivalent to Seq.fill(6)((-1).toByte)
      {Enum.map(1..6, fn _ -> -1 end), Enum.map(1..6, fn _ -> -1 end), false},
      # Equivalent to Seq.fill(7)((-1).toByte)
      {Enum.map(1..7, fn _ -> -1 end), Enum.map(1..7, fn _ -> -1 end), false},
      # (Seq(1.toByte) ++ Seq.fill(6)((-1).toByte), Seq.fill(6)(1.toByte) ++ Seq((-1).toByte))
      {[1] ++ Enum.map(1..6, fn _ -> -1 end), Enum.map(1..6, fn _ -> 1 end) ++ [-1], false}
    ]

    for {seq_a, seq_b, expected} <- test_cases do
      got = f(seq_a, seq_b)
      IO.puts("#{inspect(seq_a)} #{inspect(seq_b)} expected: #{inspect(expected)}, got: #{inspect(got)}")
    end
  end
end

# If you want to invoke the tests when running the file, you can call:
Main.run_tests()

Haskell + hgl, 41 bytes

mx<(zWn~<rv)<(no lt0*^*q0<gj)<<scp<<sQ<tx

Attempt This Online!

Takes the input as a list of length 2 with 1 representing ( and -1 representing ).

I'm not convinced this is as short as possible. It does outperform a naïve conversion of the shortest pure Haskell answer. I will come back to this later and try again.

Explanation

Reflection

I'm a bit annoyed by how long this is. I also don't like the input format and feel like it's indicative of a weakness. I'd prefer to take it as two separate arguments.

C89, 79 77 69 bytes

r,c;f(*a,*b){for(c=r=1;*a&0<c;++a)c+=*a^*b++?r=-r:*a;return c==1==r;}

Using (int)1 as '(' and (int)-1 as ')'

int diff;
int count;
int check(int *a, int *b)
{
    for ( c=1, r=1; *a!=0 && 0<c; ++a, ++b )
    {
        c += (*a != *b) ? (r=-r) : *a;
    }
    return (c==1) == r;
}

The logic is based on the consideration that if a solution exists, it will get a count on the nodes of different values lower than what it would get by alternating between '(' and ')', so that would also be a solution

Rust, 204 179 147 132 123 98 90 80 bytes

|a,b|a.iter().zip(b).fold([0;3],|[c,d,f],(&e,g)|[c+e+g,d^e^g,f.min(c+d)])==[0;3]

Attempt This Online!

Radically new algorithm. We know the only possible failure case is if:

We use d to store if there currently is an odd or even number of unequal pairs. If the nesting level c-d is negative, we fail. If the nesting level is not 0 or the number of unequal pairs is not even at the end we fail.

d^((e^!g)+1)/2 should be 1 if e=g and 0 otherwise.

Uses 1 to represent an opening parenthesis and -1 for closing.

JavaScript (Node.js), 47 bytes

Input: array, 2([), -2(])

Output: integer, 0(balanced), truthy(not)

a=>b=>a.some((v,i)=>0<(S=v-b[i]?S^1:S-v),S=0)|S

Try it online!

JavaScript (Node.js), 52 bytes

a=>b=>!a.some((v,i)=>1<(S-=v-b[i]?j^=-2:v),S=j=1)==S

Try it online!

      1 if true
      0 if some ")" mismatched, in which case S>1 so S!=0
      |
a=>b=>!a.some((v,i)=>1<(S-=v-b[i]?j^=-2:v),S=j=1)==S
                     |
                     '-- If more ")" appeared

Ruby, 108 bytes

Brute forces all combinations of the two inputs until it finds one that is balanced. Input is an array containing two arrays of integers, where 1 represents ( and -1 represents ).

->a{(v=0,1).product(*[v]*~-a[0].size).any?{i=-1;j=k=0;_1.all?{|n|(j+=a[n][i+=1])|(k+=a[1-n][i])>=0}&&j|k<1}}

Attempt This Online!

Perl 5, 53 bytes

sub{y/(/#/for@_;((pop)&pop)=~/^(#((!?)(?1)*\3)\))+$/}

Try it online!

First (wrong) version is in editing history, this one seems to pass. Please tell if it fails some test.

Nekomata + -e, 8 bytes

Ťᵐ↕∫Ɔž∑≤

Attempt This Online!

Takes input as a list of lists of -1 and 1 representing ( and ) respectively. For example, [[-1,1,1,1],[-1,-1,-1,1]] represents ())) ((().

Ťᵐ↕∫Ɔž∑≤
Ť           Transpose
 ᵐ↕         Non-deterministically permute each inner list
   ∫        Cumulative sum
    Ɔ       Split into the last element and the rest
     ž      Check if the last element is all 0s
      ∑     Sum; since the last element is all 0s, this will be 0
       ≤    Is the rest less than or equal to this sum (0)?

Charcoal, 26 bytes

∧⁼№⁺θη(Lθ⬤θ›№⁺…θ⊕κ…η⊕κ(|κ¹

Try it online! Link is to verbose version of code. Outputs a Charcoal boolean, i.e. - for balanced, nothing if not. Explanation:

    θ                       First input
   ⁺                        Concatenated with
     η                      Second input
  №                         Count of
      )                     Literal string `)`
 ⁼                          Equals
        θ                   First input
       L                    Length
∧                           Logical And
          θ                 First input
         ⬤                  All characters satisfy
               θ            First input
              …             Truncated to length
                 κ          Current index
                ⊕           Incremented
             ⁺              Concatenated with
                   η        Second input
                  …         Truncated to length
                     κ      Current index
                    ⊕       Incremented
            №               Count of
                      (     Literal string `(`
           ›                Is greater than
                        κ   Current index
                       |    Bitwise Or
                         ¹  Literal integer `1`

The idea here is that the strings can be balanced if the following two conditions hold:

  1. Exactly half of all of the characters are (s.
  2. For each prefix of both strings, there are more (s than the 0-indexed position of the last character of the prefix, plus 1 if that is even.

Examples:

1st  2nd  (s   Needed

Valid:

(    (    2    >1
()   ((   3    >1
())  (((  4    >3
())) ((() 4    >3

Invalid:

(    (    2    >1
((   ()   3    >1
(()  ())  3    >3
(()) ())( 4    >3

Haskell, 77 73 bytes

o a b=all(<1)$scanr(%)0$3:zipWith(+)a b
3%c= -c
0%c=c+1-2*mod c 2
n%c=c+n

Attempt This Online!

The other Haskell entry sets the precedent that (=1 and )=-1. The right-to-left running totals of A and B are to stay <1 (and end >-1, as checked by 3%). A+B has all information we need. Wlog the running total of A-B stays in [0,2]. scanr(%)0 maps A+B to the running total of A+B + (A-B)/2.

JavaScript (Node.js), 59 bytes

a=>b=>(h=u=>u<d|!a[i]?u|d:h(u+=p=a[i]+b[i++],d^=!p))(i=d=0)

Try it online!

Input arrays of numbers: 0.5 for (, -0.5 for ). Output falsy (0) for "can be balanced", truthy (non-zero) for "cannot be balanced".

Construct a string c using inputs a and b by:

Test if c is balanced and there are even number of mismatching.

In above code, u-d is how many unclosed "(" in string c. d is a boolean, truthy when next mismatch should be "(".

I’m feeling its correctness. Although I don’t have a mathematical proof to it. However, at least no testcase rejects it currently.

JavaScript, 109 107 ... 95 bytes

Thanks to @Arnauld for saving 2 bytes

b=(x,k=0n)=>k<0?0:x?b(x/4n,k+x%4n-2n):!k
f=(l,r,k=0n,x=k&(r^l))=>b(l^x)&b(r^x)||k<l&&f(l,r,++k)

returns false (for false) or 1 (for true).

Encodes expressions as base four numbers with 3 for ( and 1 for )

Helper functions for converting strings to the corresponding base 4 numbers

e=(s)=>s?4n*e(s.slice(1))+(s[0]=='('?3n:1n):0n
h=(x,y)=>f(e(x),e(y))

h(")))(((","((()))") // -> false

Attempt This Online!

I was unable to verify the last two Test-cases, as the function exceeded the maximum recursion depth (which as far as I known is allowed for code-golf)

Explanation

b checks if a the expression encoded by the given number is balanced, by counting the difference of 3 digits minus 1 digits and returning 0 if the intermediate count goes below zero, or if the final count is not exactly zero.

f checks for all binary numbers k less than l (swapping the outer brackets cannot balance the expression) if swapping the bits of the two inputs at the positions that are one in k will result in a pair of balanced strings. Swapping a bit has no effect if it is the low bit of a base 4 digit where both numbers have a 1 bit, if it is the high bit it will swap the characters that the given positions in the strings.


JavaScript, 108 bytes

b=(x,k=0n)=>k<0?0:x?b(x/4n,k+x%4n-2n):!k
f=l=>r=>eval("for(v=0,k=0n;k<l;k++)v|=b(l^k&(r^l))&b(r^k&(r^l));v")

uses for-loop to prevent crash due to recursion limit

Attempt This Online!

Retina 0.8.2, 144 bytes

 |^
$&#
+%`^(((\()|(?<-3>\)))*)#(.)(.* ((\()|(?<-7>\)))*)#(.)
$1$4#$5$8#$'¶$1$8#$5$4#
G`^((\()|(?<-2>\)))*(?(2)$)# ((\()|(?<-4>\)))*(?(4)$)#$
^.

Try it online! Link includes test cases. Explanation:

 |^
$&#

Place markers at the start of each string to determine progress through them.

^(((\()|(?<-3>\)))*)#(.)(.* ((\()|(?<-7>\)))*)#(.)
$1$4#$5$8#$'¶$1$8#$5$4#

If the current prefix of both strings is a validly balanced prefix, then progress through the string, generating results with and without a swap of the next character. Testing of balanced prefixes is unsurprisingly performed using .NET's balancing groups.

+%`

Repeat the above separately on each intermediate result until no new results can be obtained.

G`^((\()|(?<-2>\)))*(?(2)$)# ((\()|(?<-4>\)))*(?(4)$)#$

Keep only those results that progressed through the whole string and also are completely balanced rather than simply a balanced prefix (using conditional regex to enforce that the balancing group is now empty).

^.

Check that we had at least one result.

Haskell, 139 bytes

g=(\c->all(>=0) c&&last c==0).scanl(+)0
o j k=any(\(d,e)->g d&&g e).foldr(\(x,y)h->concat[[(x:a,y:b),(y:a,x:b)]|(a,b)<-h])[([],[])]$zip j k

foldr(\(x,y)h->concat[[(x:a,y:b),(y:a,x:b)]|(a,b)<-h])[([],[])]$zip j k gives a list of all possible pairs. o then uses g, which determines if a string is balanced, to get the final result. Input is taken as a sequence, with 1 representing '(' and −1 representing ')'.

Attempt This Online!

Scala, 165 136 bytes

Port of @mousetail's Rust answer in Scala.


Golfed version. Try it online!

Saved 29 bytes thanks to the comment of @corvus_192

(a,b)=>(a zip b)./:(Seq((0,0))){case(c,(z,y))=>c.flatMap{case(f,v)=>Seq((f+z,v+y),(f+y,v+z))}filter(g=>g._1>=0&&g._2>=0)}exists((0,0)==)

Ungolfed version. Try it online!

object Main {
  def main(args: Array[String]): Unit = {
    val f: (Seq[Byte], Seq[Byte]) => Boolean = (a, b) => {
      a.zip(b).foldLeft(Seq[(Int, Int)]((0,0))) { (c, de) =>
        c.flatMap { f => 
          Seq((f._1 + de._1, f._2 + de._2), (f._1 + de._2, f._2 + de._1))
        }.filter(g => g._1 >= 0 && g._2 >= 0)
      }.exists(_ == (0, 0))
    }

    val testCases = Seq(
      (Seq(1.toByte), Seq((-1).toByte), false),
      (Seq(1.toByte, (-1).toByte), Seq(1.toByte, (-1).toByte), true),
      (Seq(1.toByte, 1.toByte, 1.toByte, (-1).toByte, (-1).toByte, (-1).toByte), Seq(1.toByte, (-1).toByte, 1.toByte, (-1).toByte, 1.toByte, (-1).toByte), true),
      (Seq(1.toByte, (-1).toByte, 1.toByte, (-1).toByte, (-1).toByte, (-1).toByte), Seq(1.toByte, 1.toByte, 1.toByte, (-1).toByte, 1.toByte, (-1).toByte), true),
      (Seq((-1).toByte, (-1).toByte, (-1).toByte, 1.toByte, 1.toByte, 1.toByte), Seq(1.toByte, 1.toByte, 1.toByte, (-1).toByte, (-1).toByte, (-1).toByte), false),
      (Seq.fill(6)((-1).toByte), Seq.fill(6)((-1).toByte), false),
      (Seq.fill(7)((-1).toByte), Seq.fill(7)((-1).toByte), false),
      (Seq(1.toByte) ++ Seq.fill(6)((-1).toByte), Seq.fill(6)(1.toByte) ++ Seq((-1).toByte), false)
    )

    for (testCase <- testCases) {
      println(s"${testCase._1} ${testCase._2} expected: ${testCase._3}, got: ${f(testCase._1, testCase._2)}")
    }
  }
}

Jelly, 13 bytes

ZŒpÄṂȯṪƊ€oṚ$Ạ

A monadic Link that accepts a pair of lists of \$\{1,-1\}\$ representing ( and ) respectively and yields \$0\$ if balancable or \$1\$ if not (i.e. outputs the inverse).

Try it online! Or see the test-suite.

How?

ZŒpÄṂȯṪƊ€oṚ$Ạ - Link: pair of lists of -1 and 1 := ) and ( respectively
Z             - transpose
 Œp           - Cartesian product
   Ä          - cumulative sums (vectorises)
       Ɗ€     - for each: last three links as a monad:
      Ṫ       -   tail
    Ṃ         -   minimum (of the rest)
     ȯ        -   {minimum} logical OR {tail}
           $  - last two links as a monad:
          Ṛ   -   reverse
         o    -   logical OR (vectorises)
            Ạ - all?